CSS In Real LifeTips, tricks and tutorials on the web’s most beautiful language2024-03-17T00:00:00Zhttps://css-irl.info/Michelle Barkercontact@michellebarker.co.ukTime to Ditch Analytics? Tracking Scripts and Web Sustainability2024-03-17T00:00:00Zhttps://css-irl.info/time-to-ditch-analytics/<p><a href="https://rootwebdesign.studio/articles/the-environmental-benefits-of-privacy-focussed-web-design/">This article on privacy-focussed web design</a> by Paul Jardine and Becky Thorn of sustainably-minded web design agency <a href="https://rootwebdesign.studio/">Root</a> raises some great points about the link between invasive tracking scripts and the carbon footprint of a website. I’ve often been frustrated by the negative performance impact of adding Google Analytics or Google Tag Manager to a site. Sites that were previously reasonably fast would subsequently perform poorly with regard to Google’s own <a href="https://web.dev/explore/learn-core-web-vitals">Core Web Vitals</a>. It seems that many of the products Google actively encourage web authors to add to their sites are paradoxically the ones that have the worst impact on performance. (Don’t get me started on YouTube videos.)</p>
<p>Like so many other aspects of how we build websites, there is unsurprisingly a link to increased carbon emissions too. External scripts embedded in your website require power to download and parse. But there’s also the data they collect on users, which is transferred across the network and stored. The amount of data they collect goes far beyond what is useful to the end user, and is undoubtedly a contributor to the <a href="https://css-irl.info/design-patterns-that-encourage-junk-data/">huge proportion of “junk data”</a> stored in datacentres worldwide.</p>
<p>The problem is, it’s just so easy to add Google Analytics to a website, with little or no regard for users’ privacy or the performance and sustainability implications. As the article notes:</p>
<blockquote>
<p>Google Analytics trackers are featured on 72.6% of the top 75,000 websites.</p>
</blockquote>
<p>To a web manager, it free. Why would they consider an alternative? But as we all know, if something’s free, <strong>you’re the product</strong> — or rather, your users are.</p>
<p>I believe that if we care about restoring trust in the web, one of the steps we need to take is to stop productising users. After all, the current trajectory is one of ever-increasing <a href="https://pluralistic.net/2023/01/21/potemkin-ai/#hey-guys">enshittification</a>.</p>
<p>One way we can start is by having conversations with those responsible for managing the websites we build, and offering lightweight alternatives to Google Analytics for cases when data collection is absolutely necessary. (A few alternatives are listed in the article.) In many cases it may be possible to do away with client-side analytics entirely. I recently made the case for this at work while making improvements to the company website, and my manager admitted that they rarely look at the analytics. I’m willing to bet that’s a case for a lot of people.</p>
<p>Websites need to provide a clear and unambiguous way for users to opt out of unnecessary data collection to comply with <a href="https://gdpr.eu/what-is-gdpr/">GDPR legislation</a>. Analytics and tracking scripts account for the proliferation of cookie notices users are forced to interactive when they land on a site, resulting in the web feeling increasingly unusable at times. Removing tracking scripts may mean that you can remove unsightly cookie consent notices — and provide users with a better, faster experience, as well as a more sustainable one.</p>
<p><a class="o-hotlink" href="https://rootwebdesign.studio/articles/the-environmental-benefits-of-privacy-focussed-web-design/" target="_blank">Read the article by Root</a></p>
Talking About Web Sustainability on ShopTalk Show2024-03-16T00:00:00Zhttps://css-irl.info/talking-about-web-sustainability-on-shoptalk-show/<p>When you get an invite from Chris and Dave to appear on <a href="https://shoptalkshow.com/">ShopTalk Show</a> it’s hard to say no! Despite my longstanding fear of sounding like an idiot on the airwaves, I swallowed my nerves and joined them for an episode on web sustaibaility — althought we managed to delve into a few other topics too. Naturally, right afterwards I thought of a whole bunch of things I forgot to mention (hopefully I’ll get around to writing one or two follow-up blog posts!), but I was pleased to get the chance to talk about this important subject on such an esteemed podcast. I hope listeners will take away one or two things, and be inspired to do their own research.</p>
<p><a class="o-hotlink" href="https://shoptalkshow.com/606/" target="_blank">Listen here</a></p>
Creating Color Palettes with the CSS color-mix() Function2024-03-08T00:00:00Zhttps://css-irl.info/creating-color-palettes-with-the-color-mix-function/<p>Colors can sometimes get out of hand in a project. We often start with a few well-chosen brand colors, but over time, we may find ourselves adding variations as our project grows. Perhaps we realize that we need to adjust the lightness of a button color for accessibility reasons, or that we need a slightly different variant of a component. How do we ensure that the colors we choose fit within the design system for our project?</p>
<p>I’ve been exploring using the relatively new CSS <code>color-mix()</code> function for this purpose. It’s been fun to see the different palette variations I could generate! In this article for MDN we’ll explorer how <code>color-mix()</code> can be a game-changer for your design process.</p>
<p><a class="o-hotlink" href="https://developer.mozilla.org/en-US/blog/color-palettes-css-color-mix/" target="_blank">Read the article on MDN</a></p>
February 2024 Bookmarks2024-02-27T00:00:00Zhttps://css-irl.info/february-2024-bookmarks/<p>I was just collating all the articles I’ve read over the past month in order to post them here when, coincidentally, Matthias Ott’s latest <a href="https://buttondown.email/ownyourweb">Own Your Web</a> newsletter lands in my inbox! This month it’s all about bookmarks: how you collate them, where you keep them, and how you share them. I definitely need a better way to organise mine, which is why I’m sharing them here.</p>
<p>Here’s a bunch of stuff I’ve found interesting this month:</p>
<h2>Web and CSS</h2>
<ul>
<li><a href="https://heydonworks.com/article/what-is-utility-first-css/">What is Utility-First CSS?</a> by Heydon Pickering</li>
<li><a href="https://ericportis.com/posts/2024/okay-color-spaces/">Okay, Color Spaces</a> by Eric Portis</li>
<li><a href="https://rosswintle.uk/2024/02/a-manifesto-for-small-static-web-apps/">A manifesto for small, static, web apps</a> by Ross Wintle</li>
<li><a href="https://piccalil.li/blog/reality-check-3-building-out-a-layered-hero-grid-layout-from-dribbble/">Reality Check #3: Building out a layered hero grid layout from Dribbble</a> by Andy Bell</li>
<li><a href="https://remysharp.com/2024/02/23/why-my-code-isnt-in-typescript">Why my code isn't in TypeScript</a> by Remy Sharp</li>
<li><a href="https://robbowen.digital/wrote-about/abandoned-side-projects/">It's OK to abandon your side-project</a> by Robb Owen</li>
<li><a href="https://buttondown.email/ownyourweb/archive/issue-10/">Own Your Web – Issue 10: Links Worth Sharing</a> by Matthias Ott</li>
<li><a href="https://infrequently.org/2024/02/home-screen-advantage/">Home Screen Advantage: Decoding Apple's Ploy To Scuttle Progressive Web Apps</a> by Alex Russell</li>
<li><a href="https://ishadeed.com/article/css-has-guide">CSS :has() Interactive Guide</a></li>
</ul>
<h2>Sustainability</h2>
<ul>
<li><a href="https://hackernoon.com/carbon-aware-computing-next-green-breakthrough-or-new-greenwashing">Carbon Aware Computing: Next Green Breakthrough or New Greenwashing?</a> by Ismael Velasco</li>
<li><a href="https://www.theguardian.com/world/2024/feb/15/power-grab-hidden-costs-of-ireland-datacentre-boom">Power grab: the hidden costs of Ireland’s datacentre boom</a> by Jessica Traynor</li>
<li><a href="https://asemakula.medium.com/how-ad-platforms-like-facebook-google-and-others-sneakily-drive-climate-change-152194836e81">How ad platforms like Facebook, Google, and others drive climate change</a> by Abdul Semakula</li>
<li><a href="https://www.salon.com/2024/02/23/plastic-experts-say-recycling-is-a-scam-should-we-even-do-it-anymore/">Plastic experts say recycling is a scam. Should we even do it anymore?</a> by Matthew Rozsa</li>
</ul>
<h2>AI and tech criticism</h2>
<ul>
<li><a href="https://jenniferplusplus.com/losing-the-imitation-game/">Losing the imitation game</a> by Jennifer Moore</li>
<li><a href="https://disconnect.blog/how-artists-are-fighting-generative-ai/">How artists are fighting generative AI</a> by Paris Marx</li>
<li><a href="https://daverupert.com/2024/02/robo-barf/">A dozen thoughts about AI</a> by Dave Rupert</li>
<li><a href="https://www.techdirt.com/2024/02/20/how-allowing-copyright-on-ai-generated-works-could-destroy-creative-industries/">How Allowing Copyright On AI-Generated Works Could Destroy Creative Industries</a> by Glyn Moody</li>
<li><a href="https://www.onceupondata.com/post/2024-02-18-enshittification-of-everything/">On The Enshittification of Everything!</a> by Omayma Said</li>
<li><a href="https://medium.com/@emilymenonbender/on-nyt-magazine-on-ai-resist-the-urge-to-be-impressed-3d92fd9a0edd">On NYT Magazine on AI: Resist the Urge to be Impressed</a> by Emily M Bender</li>
<li><a href="https://www.theverge.com/c/23194235/ai-fiction-writing-amazon-kindle-sudowrite-jasper">The Great Fiction of AI</a> by Josh Dzieza</li>
<li><a href="https://www.citationneeded.news/review-read-write-own-by-chris-dixon/">Review: Chris Dixon's Read Write Own</a> by Molly White</li>
</ul>
<h2>Demos</h2>
<ul>
<li>Alvaro Montoro’s collection of CSS <a href="https://codepen.io/collection/aMPYMo">toggles</a></li>
<li>Temani Afif’s animated flower shapes — <a href="https://codepen.io/t_afif/full/BabgjVO">one example</a></li>
</ul>
Design Patterns that Encourage Junk Data2024-02-20T00:00:00Zhttps://css-irl.info/design-patterns-that-encourage-junk-data/<p>A post <a href="https://front-end.social/@rem/111957249492228263">from Remy</a> on Mastodon recently got me thinking:</p>
<blockquote>
<p>Been picking up some of the jsbin archive work. Today, there's currently 62 millions bins stored. The last full copy archive I ran (which downloads the full html, js + css into a single page) holds 42 millions bins, consumes 130gb and the names alone (not even the urls) takes up 750mb.</p>
</blockquote>
<p>That’s a lot of data, and I’d be willing to bet a fair amount of it is no longer needed.</p>
<p>Not long ago I read <a href="https://gerrymcgovern.com/world-wide-waste/">World Wide Waste</a> by Gerry McGovern, which brings into sharp relief the physical and ecological impacts of our digital lives. One aspect of this is the huge amount of data we accumulate. It all has to be stored somewhere. And despite what we might like to believe — that “the cloud” is this magical, nebulous, (crucially) free storage space floating high above us — it’s actually stored in vast datacentres, hungry for water and electricity. The Guardian recently ran a story on <a href="https://www.theguardian.com/world/2024/feb/15/power-grab-hidden-costs-of-ireland-datacentre-boom">datacentres in Ireland</a>, which are estimated to consume 18% of the country’s electricity. And more are being built all the time, to satisfy our ever-growing appetite for data.</p>
<p>The thing is, we don’t really need all this storage. It’s estimated that <a href="https://volume.lboro.ac.uk/digital-waste-polluting-the-planet">up to 88%</a> of the data stored in the cloud is ROT (Redundant, Obsolete or Trivial) data, or <a href="https://www.gartner.com/en/information-technology/glossary/dark-data">“dark data”</a>: data collected by companies in the course of their regular business activities, but which is not used for any other purpose. It all amounts to a lot of junk data that has no purpose, that will never be needed or looked at again. Think how many photos we snap away on our hi-res smartphones, instantly uploaded to the cloud and forgotten. Yet storing this data requires construction materials, rare metals, land and human labour to build the facilities to store it, as well as water and electricity to keep them powered and cooled. All so we can avoid the inconvenience of deleting stuff.</p>
<p>But it’s also really easy to create a whole lot of junk data unintentionally. Remy’s web app, JS Bin is incredibly useful when you just want to quickly test out some HTML, CSS or JS in the browser, without setting up a load of boilerplate and a local web server. I use it for exactly that. Sometimes I might send someone the link to refer to. I almost <em>never</em> need to look at that demo ever again after that day. And yet that data is stored indefinitely. It rarely occurs to me to go back and delete it when I’m done.</p>
<p>This isn’t intended as a criticism of Remy’s excellent work. It’s a fantastic service, and there are no doubt many people who use JS Bin very differently, who value the ability to revisit their bins much later. It’s just one of many, many apps that make it very easy for us to create data that lives (virtually) forever. Does it need to? I’m not sure. But we’ve come to expect that in all of our online services, and that’s not sustainable.</p>
<h2>What does a better model look like?</h2>
<p>The question is, how do we design our products and services so that it’s easier and more convenient for users to delete junk data, or even better, to avoid creating it in the first place?</p>
<p>One possibility is to introduce more friction into the process. For example, requiring a user to create an account before their work is saved would almost certainly reduce the amount of data created in the case of apps like JS Bin. But it would also remove a big chunk of what’s appealing about it: the fact that you don’t <em>need</em> to go through the time-consuming process of creating an account. So is there another happy medium, such as time-limited storage for data not tied to a user account? It’s certainly becoming a more pressing question, as the need for limitless digital storage bumps up against the very real physical limits of our planet.</p>
<p>This is where I think design needs to step up. Take email. Most of us have thousands of old emails languishing in our inboxes, stored indefinitely. After a certain point, the thought of going through and deleting them becomes too arduous. Email clients could be designed in a way that encourages (or even requires) users to configure settings for automatic deletion after a certain time period, with a “safe list” for important ones. I’m sure that with more than half an hour’s thought, even better solutions could be found.</p>
<p>When companies make it so easy for us to create junk data at virtually no cost, while making huge profits themselves, they shouldn’t be permitted to pass the cost of cleaning it up onto the individual. They need to take responsibility for their own sprawling data problem, and its planetary impact.</p>
<h2>Resources</h2>
<ul>
<li><a href="https://www.theguardian.com/world/2024/feb/15/power-grab-hidden-costs-of-ireland-datacentre-boom">Power grab: the hidden costs of Ireland’s datacentre boom</a> (The Guardian)</li>
<li><a href="https://volume.lboro.ac.uk/digital-waste-polluting-the-planet">How digital waste is polluting the planet</a></li>
<li><a href="https://theconversation.com/dark-data-is-killing-the-planet-we-need-digital-decarbonisation-190423">‘Dark data’ is killing the planet – we need digital decarbonisation</a> (The Conversation)</li>
<li><a href="https://digitaldecarb.org/">Digital Decarbonisation</a></li>
</ul>
How I Solved My Font Rendering Problem2024-02-13T00:00:00Zhttps://css-irl.info/how-i-solved-my-font-rendering-problem/<p>Since I redesigned this website last year, an issue with the heading font has been bugging me. I’d noticed that, unlike in other browsers, in Safari on iOS the headings rendered poorly, slightly blurry, as if they’d been faux-bolded. Googling the problem was coming up with nothing, but I’m using a variable font, so I figured perhaps that had something to do with it not being a super-common issue.</p>
<figure>
<img src="https://css-irl.info/font-rendering_900.webp" width="1600" height="900" srcset="https://css-irl.info/font-rendering_1600.webp 1600w, https://css-irl.info/font-rendering_1200.webp 1200w, https://css-irl.info/font-rendering_900.webp 900w" sizes="(max-width: 1080px) 90vw, 930px" alt="Comparing two screenshots of heading and body font rendering. In the one on the left, the headings look oddly chunky and distorted." />
<figcaption>Font rendering in iOS: Before and after fixing.</figcaption>
</figure>
<p>I’m happy to say, I’ve figured it out and finally implemented a fix. It involved learning a thing or two about <code>@font-face</code>.</p>
<p>The <code>@font-face</code> at rule is where we specify a font for use in our CSS. Most of the time I don’t give it too much thought. I specify a few things:</p>
<ol>
<li>The font family (How you want to refer to it in the CSS <code>font-family</code> property. Usually the name of the font, but it can be anything you want.)</li>
<li>The <code>src</code> URL and font format. These days I tend to just go with WOFF2, as it’s well supported. Old browsers can fall back to a system font with no harm to the user.</li>
<li><code>font-display</code>. This determines what happens during the period before the font has downloaded. I go with <code>font-display: swap</code>, which according to MDN, “gives the font face an extremely small block period and an infinite swap period.” In practice that means that during the font block period (before the font is downloaded) a fallback font will be used after a very short delay (up to 100ms <a href="https://drafts.csswg.org/css-fonts/#font-display-desc">according to the spec</a>), which will then be swapped for the specified font once it’s downloaded.</li>
</ol>
<pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@font-face</span></span> <span class="token punctuation">{</span>
<span class="token property">font-family</span><span class="token punctuation">:</span> <span class="token string">'Urbanist'</span><span class="token punctuation">;</span>
<span class="token property">src</span><span class="token punctuation">:</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span><span class="token string url">'../fonts/urbanist.woff2'</span><span class="token punctuation">)</span></span> <span class="token function">format</span><span class="token punctuation">(</span><span class="token string">'woff2'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">font-display</span><span class="token punctuation">:</span> swap<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>There are a number of other properties that can be specified in the <code>@font-face</code> rule, not all of them necessary in all cases. But with variable fonts we need at least couple of things in order for our fonts to render reliably.</p>
<p>If our font variation is on the <strong>weight</strong> axis, we should set a range for the <code>font-weight</code> property. A range of 100 to 800 permits us to use any weight in that range (assuming they’re available in our font file).</p>
<pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@font-face</span></span> <span class="token punctuation">{</span>
<span class="token property">font-family</span><span class="token punctuation">:</span> <span class="token string">'Urbanist'</span><span class="token punctuation">;</span>
<span class="token property">src</span><span class="token punctuation">:</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span><span class="token string url">'../fonts/urbanist.woff2'</span><span class="token punctuation">)</span></span> <span class="token function">format</span><span class="token punctuation">(</span><span class="token string">'woff2'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">font-display</span><span class="token punctuation">:</span> swap<span class="token punctuation">;</span>
<span class="token property">font-weight</span><span class="token punctuation">:</span> 100 800<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Secondly, it’s recommended that we set <code>'woff2-variations'</code> as the font format. In practice the font rendered fine with just the <code>font-weight</code> addition above, and <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_fonts/Variable_fonts_guide">MDN states that this isn’t strictly necessary</a>. But what the hell, it’s a recommendation, so let’s add it. Now the <code>@font-face</code> declaration looks like this:</p>
<pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@font-face</span></span> <span class="token punctuation">{</span>
<span class="token property">font-family</span><span class="token punctuation">:</span> <span class="token string">'Urbanist'</span><span class="token punctuation">;</span>
<span class="token property">src</span><span class="token punctuation">:</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span><span class="token string url">'../fonts/urbanist.woff2'</span><span class="token punctuation">)</span></span> <span class="token function">format</span><span class="token punctuation">(</span><span class="token string">'woff2-variations'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">font-display</span><span class="token punctuation">:</span> swap<span class="token punctuation">;</span>
<span class="token property">font-weight</span><span class="token punctuation">:</span> 100 800<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>And now my fonts look beautiful everywhere!</p>
New and Improved Green Web Hosting Directory from the Green Web Foundation2024-02-12T00:00:00Zhttps://css-irl.info/new-and-improved-green-web-hosting-directory/<figure>
<img srcset="https://css-irl.info/green-web-foundation-directory_1600.webp 1600w, https://css-irl.info/green-web-foundation-directory_1200.webp 1200w, https://css-irl.info/green-web-foundation-directory_900.webp 900w, https://css-irl.info/green-web-foundation-directory_600.webp 600w" sizes="(min-width: 1200px) 1200px, 90vw" src="https://css-irl.info/green-web-foundation-directory_1200.webp" width="1600" height="900" alt="Screenshot of the Green Web Foundation’s hosting directory" />
</figure>
<p>The Green Web Foundation has recently redesigned their <a href="https://app.greenweb.org/directory/">green web hosting directory</a>. Previously the directory was a useful resource for finding hosting platforms that at least claim some sustainable credentials but, <a href="https://css-irl.info/choosing-a-green-web-host/">as I’ve noted before</a> the information provided by each web host was somewhat limited. From the directory alone it was impossible to discern which providers could claim to be running on renewable energy rather than buying carbon offsets, for instance, or which providers were doing the bare minimum of box-ticking, as opposed to making sustainability a key part of their offering. Finding a truly green web host is hard!</p>
<p>The revamped directory prioritises some key improvements, as Chris Adams explains in <a href="https://www.thegreenwebfoundation.org/news/introducing-our-updated-directory/">his blog post</a> introducing the changes. Firstly, you can now filter hosting providers by country and by the <strong>type of hosting</strong> you require, as different users understandably might have very different hosting needs.</p>
<p>Secondly, providers are encouraged to include supporting evidence for their green claims, which is published in the directory with their listing. Good supporting evidence is something that would make me much more inclined towards giving my money to a company on the list.</p>
<p>Thirdly, listings without supporting evidence display a call to action button, offering companies and users to help improve the listing by submitting a correction or nudging the company into action. This is a great collaborative step, and I hope people will use it to encourage companies to be more transparent about their claims.</p>
<p><a href="https://app.greenweb.org/directory/">Visit the directory</a></p>
Eleventy Starter Project Updates2024-02-11T00:00:00Zhttps://css-irl.info/eleventy-starter-projects-updates/<figure>
<img srcset="https://css-irl.info/eleventy-starter_1600.webp 1600w, https://css-irl.info/eleventy-starter_1200.webp 1200w, https://css-irl.info/eleventy-starter_900.webp 900w, https://css-irl.info/eleventy-starter_600.webp 600w" sizes="(min-width: 1200px) 1200px, 90vw" src="https://css-irl.info/eleventy-starter_1200.webp" width="1600" height="900" alt="Eleventy Parcel v2.2.0 screenshot" />
</figure>
<p>This blog uses static site generator <a href="https://www.11ty.dev/">Eleventy</a> (or 11ty. I have no idea which one is the “official” spelling, and the docs appear to delight in switching gratuitously between the two! Let’s go with “Eleventy” for now.) under the hood. I like Eleventy because it allows me to write blog posts in markdown and use <a href="https://mozilla.github.io/nunjucks/">Nunjucks</a> for templates, which get built into HTML file on deployment and served as static files, so it’s nice and lightweight on the client side. I also have an <a href="https://github.com/mbarker84/eleventy-parcel">Eleventy starter project</a> that’s been long-overdue for some maintenance. I recently made a few updates, so I thought I’d note them down here.</p>
<p>Out of the box, Eleventy doesn’t apply any kind of bundling or optimisation to your CSS and JS files — it’s up to you, the developer to bundle those if you wish. Now, perhaps you don’t <strong>need</strong> any kind of bundling of those files. It’s perfectly fine to serve up raw CSS and JS for a small project, and the Eleventy docs explain <a href="https://www.11ty.dev/docs/assets/">how to do that</a>. To be honest, we could probably get away with that for this blog: even in their unoptimised form, the CSS and JS files would likely be smaller that those of most websites. But I wanted my starter project to be scaleable. I’ve been using <a href="https://parceljs.org/">Parcel</a> to bundle CSS and JS files, but I knew I could make some improvements to streamline the build process.</p>
<h2>What’s changed?</h2>
<h3>Sass variables to CSS</h3>
<p>CSS custom properties are more versatile than Sass variables, as they can be changed in different contexts. Where possible, I prefer to use vanilla CSS features (when they’re well-supported). Eventually I’d like to remove reliance on Sass entirely, and this is the first step towards that.</p>
<h3>Fluid type and spacing with Utopia</h3>
<p>I’ve added custom properties for fluid typography and spacing using <a href="https://utopia.fyi/">Utopia</a>, one of my favourite tools that I find indispensable for working with modern CSS. These variables can be configured at the beginning of a project, and allow you to use values for padding, margins and typography that scale with the viewport — or even the container.</p>
<h3>SVG sprite</h3>
<p>Icons reference an SVG sprite as a separate file, rather than the sprite being inserted into the HTML of the page.</p>
<h3>Simplified build process</h3>
<p>I’ve upgraded to the latest Eleventy version, and instead of spinning up Browsersync I’m using the Eleventy dev server to watch template files and reload. As <a href="https://11ty.rocks/posts/new-features-upgrade-considerations-eleventy-version-2/#configuration-build-and-serve">Stephanie notes</a>:</p>
<blockquote>
<p>...the default server is much faster and lighter and doesn’t require loading any JS during watch and serve local dev.</p>
</blockquote>
<p>Because I’m using Parcel to bundle CSS and JS files, I just needed to add the source files as watch targets for Eleventy (in <em>.eleventy.js</em>), so that any changes to those will trigger a reload too.</p>
<pre class="language-js"><code class="language-js">module<span class="token punctuation">.</span><span class="token function-variable function">exports</span> <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">eleventyConfig</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
eleventyConfig<span class="token punctuation">.</span><span class="token function">addWatchTarget</span><span class="token punctuation">(</span><span class="token string">'./src/css/'</span><span class="token punctuation">)</span>
eleventyConfig<span class="token punctuation">.</span><span class="token function">addWatchTarget</span><span class="token punctuation">(</span><span class="token string">'./src/js/'</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span></code></pre>
<p>I’ve also simplified things by taking advantage of Eleventy’s <code>addPassthroughCopy</code>, which copies files or directories over to the deploy folder, instead of using Node to do this (as was the case in the previous version).</p>
<h3>Blocking bots with robots.txt</h3>
<p>I’ve added a <em>robots.txt</em> file that’s copied over to the output directory at build time. It has a few rules for blocking some known AI user agents so (theoretically) the site can’t be used to train AI large language and diffusion models.</p>
<h2>What’s stayed the same?</h2>
<h3>Sass</h3>
<p>The project still uses Sass, and Parcel makes it really easy to transform Sass to CSS with its transformer plugin. The main reason I’m still using Sass is for breakpoint variables (which aren’t possible with CSS) and nesting, which only became universally supported in vanilla CSS very recently. In a future iteration I’ll probably convert the whole project to vanilla CSS and do away with Sass entirely.</p>
<p>If you wanted to use the starter project with just vanilla CSS, you could swap out the Sass file for a CSS file (making sure to update the file path in <em>layout.njk</em>, and the source file in <em>package.json</em>).</p>
<h3>Cache-busting</h3>
<p>CSS and JS filenames are hashed on build using a Node script. Someone has made an <a href="https://github.com/mightyplow/eleventy-plugin-cache-buster">Eleventy plugin</a> for this which I guess I could implement instead, but my own code seems to work just fine, and it’s nice not to worry about an extra dependency.</p>
<h2>Issues</h2>
<p>You now need to run two commands to run the project locally, in order to run the Eleventy dev server <strong>and</strong> have Parcel watch CSS and JS files. I changed this because I was having a repeated issue where Eleventy would not respond the <kbd>CTRL</kbd> + <kbd>C</kbd> in the terminal and would continue to run. I assumed it was something to do with running Parcel at the same time, but in fact it still happens when I run Eleventy about 50% of the time. No idea why, I suspect it might be something to do with my Node environment.</p>
<p>Anyway, that’s my only gripe. I’m pretty happy with my setup!</p>
Wrapping Up 20232024-01-23T00:00:00Zhttps://css-irl.info/wrapping-up-2023/<figure>
<img src="https://css-irl.info/wrapping-up-2023.svg" alt="2023 scrolling out to the left, 2024 scrolling in from the right" width="1600" height="900" />
</figure>
<p>I’ve held off publishing a “year in review” post until now because, to be honest, towards the end of last year I wasn’t feeling too optimistic about the state of the web. Normally I like to end the year with a (mostly) upbeat reflection on my experiences, and on new developments in CSS. But for the first time, I feel a sense of trepidation for my chosen profession, and the industry as a whole. It seems every week the headlines are filled with new ways that companies are using AI to exploit workers and fill the web with soulless marketing soup. Additionally, I’ve seen several friends and acquaintances laid off this year, and I have a feeling that trend might continue a while yet. It’s taken a bit of the shine of my enthusiasm for the web, even to the point of questioning whether my chosen profession is still where I want to be.</p>
<p>Thankfully a few weeks downtime and reflection (read: parenting and clearing out several years worth of junk from my house) has left with me with a renewed enthusiasm for creativity and coding, even though it comes with a healthy dose of skepticism (or cynicism?) for the tech industry. So let’s proceed with a belated 2023 in review, and a little peek into 2024...</p>
<h2>Personal achievements</h2>
<p>Let’s start with some positives, shall we? I’m proud to have met a few personal goals this year.</p>
<h3>Learning to drive</h3>
<p>I passed my driving test (on the first attempt, if you’ll allow me to brag a little 😊)! My goal was to learn by the time I turn 40, so I’m well within that window. I still don’t like driving, and begrudge the fact that I have to, but it means I can take my son places without relying on friends and family for lifts.</p>
<h3>No new clothes in 2023</h3>
<p>I set myself the goal of buying no <strong>new</strong> clothes (except socks and underwear) for myself for a whole year. This is mainly for environmental reasons — the fashion industry is incredibly polluting and exploitative. Happily I achieved that goal, although I did buy a few things from charity shops and secondhand sites. I also allowed myself to buy new clothes for my son and husband. Next year I’ll try to keep to the same goal, but also aim to source more of my son’s clothes secondhand.</p>
<h3>Reading instead of doom scrolling</h3>
<p>I made a conscious effort to read more books and be less online this year, in part because I wanted to model positive behaviour for my son. It’s been difficult at times as the allure of the screen is strong, and there have been plenty of days when I’ve fallen prey to it. But I’m definitely heading in the right direction. I’ve certainly read more books than the previous year, and feel better for it. One of my favourite books I read this year was The Ship by Antonia Honeywell. I’m a sucker for dystopian sci-fi, and this is right up my street.</p>
<h3>Drums</h3>
<p>I learnt to play Cherub Rock, one of my favourite Smashing Pumpkins songs, on the drums! 🤘 I also joined a band! We mostly play 90s grunge covers. It’s a pretty casual affair, but really nice to play with others again.</p>
<h2>Conferences</h2>
<p>I spoke at three events in 2023: <a href="https://beyondtellerrand.com/events/dusseldorf-2023/speakers">Beyond Tellerand</a> in Düsseldorf, <a href="https://heypresents.com/conferences/2023">All Day Hey</a> in Leeds, and <a href="https://smashingconf.com/meets-green">Smashing Meets Goes Green</a>, an online event. These were all great conferences. Beyond Tellerand had an awesome, inspiring mix of talks that went beyond code, into various other realms of creativity. All Day Hey had a fantastic atmosphere, and really great talks for front end developers and others working on the web. It was my first time in Leeds, which seems like a very cool city.</p>
<p>It was also the first time I gave a full-length talk on web sustainability, and I really enjoyed preparing that talk. My only regret was agreeing to give two completely different talks at two events within such a short space of time. It definitely made the preparation a little stressful!</p>
<p>I gave an updated version of that talk at Smashing Meets Goes Green, and was really pleased with how it went. I normally find online events a bit more stressful, but this one went without a hitch (from my perspective), and I thought the format of two talks followed by a panel discussion worked really well. It was a very enjoyable event.</p>
<p>In June I was back at <a href="https://cssday.nl/2023">CSS Day</a> in Amsterdam, this time as an MC (alongside <a href="https://nerdy.dev/">Adam Argyle</a>). I have to say, it was great to be able to enjoy all the talks without stressing about my own! It’s got to be one of my favourite conferences, everyone there is so enthusiastic about CSS. My kind of people! It was great to see a whole lot of familiar faces, and meet some internet friends (especially <a href="https://www.miriamsuzanne.com/">Miriam Suzanne</a> and <a href="https://thinkdobecreate.com/">Stephanie Eckles</a>) for the first time.</p>
<p>I also got to attend <a href="https://2023.stateofthebrowser.com/">State of the Browser</a> and <a href="https://ffconf.org/">FF Conf</a>. They had some of my favourite conference talks of the year:</p>
<ul>
<li><a href="https://ffconf.org/talks/2023_maggie/">The Expanding Dark Forest and Generative AI</a> by Maggie Appleton</li>
<li><a href="https://2023.stateofthebrowser.com/speaker/amy-hupe/">It All Means Nothing in the End</a> by Amy Hupe</li>
<li><a href="https://2023.stateofthebrowser.com/speaker/ana-rodrigues/">Exploring the Potential of the Web Speech API in Karaoke</a> by Ana Rodrigues</li>
</ul>
<h3>Conferences in 2024</h3>
<p>In 2024 you’ll find me speaking at <a href="https://www.middlesbroughfe.co.uk/">Middlesbrough Front End</a>, and <a href="https://pixelpioneers.co/">Pixel Pioneers</a> in my (sort of) home city of Bristol. I’ll be speaking about CSS, although I wouldn’t mind giving my web sustainability talk somewhere. Give me a shout if you have an event you think it would suit!</p>
<h2>Writing</h2>
<p>I’m writing fewer long-form CSS articles on CSS { IRL } these days, but that doesn’t mean I’ve stopped writing about CSS. I wrote several articles for <a href="https://developer.mozilla.org/en-US/blog/">MDN</a> in 2023, and hope to continue with a few more this year. I was particularly proud of <a href="https://developer.mozilla.org/en-US/blog/introduction-to-web-sustainability/">Introduction to Web Sustainability</a>, as I feel it’s really important to bring these ideas to a wider audience.</p>
<h3>NaBloPoMo</h3>
<p>November was National Blog Posting Month, and I managed to publish something every day! This is the first time I’ve done it, and I don’t know if I’d do it again. But it was surprisingly enjoyable, and got me out of a bit of a rut by forcing myself to publish something every day. Sometimes the best creativity is forced creativity 😆 I plan to channel some of that energy and be less hesitant about publishing in 2024.</p>
<h2>Work stuff</h2>
<p>It’s been rewarding to work on the <a href="https://windscope.com/">Windscope</a> platform with the Ada Mode team, and finally see it being used by paying customers. Although it’s a small team, I’m enjoying the mix of design, planning and development work that the job allows me. I don’t have any grand work ambitions for 2024, other than growing my data viz arsenal.</p>
<h2>Looking ahead</h2>
<p>I’m not one to set out huge goals and resolutions — I prefer to keep it small and manageable. Beyond what we’ve already covered, I have a couple of aims for 2024:</p>
<h3>Web sustainability</h3>
<p>I hope to continue to be active in the web sustainability community and further educate myself in this area. There are a tonne of people doing fantastic work here. I’ll try to publish what I learn on this blog.</p>
<h3>Disable the cookies</h3>
<p>One small goal for 2024 is to disable cookies (if possible) on every site I visit. Like many people, I’ve become a lot more privacy-conscious and less trusting of tech companies these days. It feels like a tiny strike in the war against the <a href="https://pluralistic.net/2023/01/21/potemkin-ai/#hey-guys">enshittification</a> of the web.</p>
Video: Building a Greener Web (Smashing Meets Goes Green)2024-01-21T00:00:00Zhttps://css-irl.info/video-building-a-greener-web-smashing-meets/<p>Just before Christmas I spoke at Smashing Meets Goes Green an online event focused on digital sustainability. The video of my talk is now online, so if you’d like to know more about this vast topic, jump right in!</p>
<p>https://www.youtube.com/watch?v=xtzcE_HUx2o</p>
<p>The second talk of the evening was by Gerry McGovern, whose fantastic book, <a href="https://gerrymcgovern.com/world-wide-waste/">World Wide Waste</a> is packed with insights into the pollution caused by our digital lifestyles. Some of the statistics in <a href="https://www.youtube.com/watch?v=owgJgRo8M9s">his talk</a> (and in the book itself) are downright terrifying, and a wakeup call to those of us working on the web. It’s well worth a watch.</p>
<p>We also participated in a <a href="https://www.youtube.com/watch?v=X3nxBhhTjwI">panel discussion</a> chaired by Vitaly. I found some of the questions tough to answer, as it’s hard to predict what will happen over the coming months and years, especially with things moving so fast in the field of AI. While Gerry is fairly unequivocal in his opinion that AI will be an overall negative force for sustainability, I’m a little more ambivalent. I can see cases where it might be useful in augmenting the green transition, not least because the company I work for is involved with AI for maintenance of wind turbines and nuclear decommissioning — two fields which could be highly dangerous for a human working manually, and where AI has the potential to accelerate the technologies required. Gerry made some great points about how increasing the technology tend to just increase the amount of energy expended, or the amount of stuff produced though (paraphrasing). Sometimes more technology isn’t the solution.</p>
<p>I don’t know anything for certain, but I’m trying to educate myself as best I can, and I love that we’re having these discussions. I hope you enjoy and learn something from them too.</p>
Where Do We Go From Here?2024-01-20T00:00:00Zhttps://css-irl.info/where-do-we-go-from-here/<p>A question I’ve been asking myself recently is “What is the purpose of this blog”? When I started writing in 2018 it didn’t occur to me that I’d still be doing it five years later. I picked the name “CSS { In Real Life }” because I loved CSS (I still do!), and that was what I was most interested in learning and writing about. But over the course of the years I’ve found myself expanding my writing into different areas: web sustainability, JS, dev ops, front-end in general, and work/life more broadly.</p>
<p>Somehow this blog became a respected source of CSS articles, thanks to some well-known folks sharing it on social media and through newsletters. But it’s first and foremost a personal blog. The idea from the beginning was to document my web development learnings, and now I’ve found that many of the things I’m learning that feel important aren’t exclusively in the realm of web development. I want to write more personally from time to time, including non-web topics: book reviews, thoughts on parenting and work/life balance, tech in general, sustainability as a broad topic (beyond the web). A blog feels like a natural fit, especially now that Twitter is no longer a viable place to share thoughts and have discussions. But I worry that visitors coming along hoping for CSS content will be disappointed.</p>
<p>That’s not to say I won’t be writing about CSS anymore. But in my day-to-day work I don’t get to play around with CSS quite so much now (it’s very JS and data-viz heavy), and my priorities in my personal life have shifted off-screen. As a consequence I don’t always have as much to write on experimental CSS or interesting use cases for features as I once did. There are a great number of wonderful people writing in-depth CSS articles: <a href="https://ishadeed.com/">Ahmad Shadeed</a>, <a href="https://thinkdobecreate.com/">Stephanie Eckles</a>, <a href="https://codepen.io/t_afif">Temani Afif</a>, <a href="https://matuzo.at/blog">Manuel Matuzović</a>, <a href="https://www.miriamsuzanne.com/">Miriam Suzanne</a>, <a href="https://kizu.dev/">Roma Komarov</a>, the CSS team at Google (<a href="https://nerdy.dev/">Adam Argyle</a>, <a href="https://una.im/">Una Kravets</a> and <a href="https://www.bram.us/">Bramus Van Damme</a>), to name just a few, and I enjoy reading and learning from their work. I want to write about more than CSS, but when the title of this blog pertains to CSS, is it appropriate?</p>
<p>I could start a separate, personal blog over on my <a href="https://michellebarker.co.uk/">personal site</a>. But I’m not sure I have the time to maintain two blogs, and if I kept this one exclusively for front end content I feel like I’d end up posting here a lot less. Maybe that’s fine though?</p>
<p>So I’m asking you, dear reader: should I keep posting here, even if the content isn’t specifically CSS, or even front end? Or should my personal blog have another home? I’m not going to go all Elon and abide by your decision no matter what. But I’m genuinely interested to hear your thoughts 🙂</p>
Workshop Review: Data Visualisation Fundamentals with Andy Kirk2024-01-16T00:00:00Zhttps://css-irl.info/workshop-review-data-visualisation-fundamentals/<p>Last week I had the privilege of attending an online workshop on <strong>Data Visualisation Fundamentals</strong> hosted by data visualisation expert <a href="https://visualisingdata.com/">Andy Kirk</a>. The six-hour workshop was split across two days, and consisted of a mixture of teaching materials, examples and interactive exercises (both individually and in small groups). It can be tricky to find the right balance to hold people’s attention for an online workshop — speaking from my own experience, I find I sometimes struggle to stay engaged compared to attending in person. But the blend of content here, combined with Andy’s presentation style and his expertise and enthusiasm for the subject meant that in this case I felt fully immersed. It appeared the rest of the audience was similarly engaged too.</p>
<h2>What did we learn?</h2>
<p>One of the most useful aspects of the workshop for me was learning about how to analyse and interrogate the data <strong>before</strong> designing the chart itself. What questions do we need to ask? What is the message we’re trying to convey? Who is the audience? Where will they encounter the visualisation? What limitations are there? What types of charts might be suitable or unsuitable? It made me think about approaching data visualisation in a new way, and consider slowing down and taking a more methodical approach.</p>
<figure>
<img src="https://css-irl.info/workshop-review-data-visualisation-fundamentals_1200.webp" alt="Sketches of three data visualisations" width="1600" height="900" srcset="https://css-irl.info/workshop-review-data-visualisation-fundamentals_1600.webp 1600w, https://css-irl.info/workshop-review-data-visualisation-fundamentals_1200.webp 1200w, https://css-irl.info/workshop-review-data-visualisation-fundamentals_900.webp 900w, https://css-irl.info/workshop-review-data-visualisation-fundamentals_600.webp 600w" sizes="(min-width: 1086px) 75vw, (min-width: 1264px) 930px, 90vw" />
<figcaption>Sketches from some of the workshop exercises</figcaption>
</figure>
<p>There were some fun group exercises analysing different charts. It was helpful to examine the choices the designers had made within the constraints, what worked well and what didn’t, and think about how they could be improved.</p>
<p>Andy presented examples of lots of different types of charts, and talked about what sort of data they could be used for. I found this really helpful, and although it was a lot of information I know I’ll refer back to it later.</p>
<p>Andy also introduced a sketching tool I hadn’t encountered before, <a href="https://excalidraw.com/">Excalidraw</a>. This seems super useful for brainstorming visualisation ideas, and I’ll definitely use it in the future. I enjoyed coming up with different chart ideas, and it was useful to apply the information from the workshop in a practical way.</p>
<h2>Summary</h2>
<p>I got a lot out of this workshop, and would recommend it for anyone interested in designing charts and levelling up their data visualisation skills. It’s very much a design-and-thinking workshop — you don’t have to be an expert at any particular tool, or be amazing at drawing. There’s plenty of relevance, regardless of whether you’re a total beginner or a seasoned pro.</p>
<p>Andy also runs a longer (in-person) version of this workshop, which seem like it would be worthwhile.</p>
How Do You Vertically Centre an Element in CSS? (Even More) Easily!2023-12-21T00:00:00Zhttps://css-irl.info/how-do-you-vertically-centre-an-element-in-css/<p>Rachel Andrew shared a <a href="https://rachelandrew.co.uk/archives/2023/12/19/align-content-in-block-layout/">snippet of good news for CSS layout</a> on her blog the other day: it’ll soon be possible to vertically centre an element inside a parent <strong>without</strong> the parent needing to be a flex or grid container, using the <code>align-content</code> property.</p>
<p>Gone are the days when developers lamented they couldn’t center a <code><div></code> (hopefully), as we have a bunch of ways to do that, the shortest of which only require two properties set on the parent using Grid or flexbox:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.parent</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span>
<span class="token property">align-items</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.parent</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token property">align-items</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>You can see the difference between the two in this demo.</p>
<p class="codepen" data-height="437.234375" data-default-tab="result" data-slug-hash="zYbOjBz" data-user="michellebarker" style="height: 437.234375px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/zYbOjBz">
Center align elements in flex and grid</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>When the child element is a flex item the resolved width will depend on the content, whereas <code>display: grid</code> will create a grid of one column spanning the available width, with the child element continuing to behave similarly to a block item and take up the entire width of the column (unless we’ve set an intrinsic width).</p>
<p>Using Grid, <code>align-content</code> will work just as well for a single item (although the effect will be different when you have several items in the grid):</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.parent</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token property">align-content</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>It won’t vertically centre a flex item though.</p>
<p>In addition, we can align an element both vertically <strong>and</strong> horizontally with <code>place-items</code> — shorthand for <code>justify-items</code> and <code>align-items</code>:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.parent</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token property">place-items</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>It’s also possible to vertically align with <code>justify-content</code> in a flex layout if we switch the flex container to use the column direction rather than the row, but this requires one extra property:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.parent</span> <span class="token punctuation">{</span>
<span class="token property">flex-direction</span><span class="token punctuation">:</span> column<span class="token punctuation">;</span>
<span class="token property">justify-content</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Once again, there’s a shorthand available to us with Grid if we want to combine <code>justify-content</code> and <code>align-content</code>:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.parent</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token property">place-content</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h2>Block layout</h2>
<p>With the news Rachel shared, the parent items wouldn’t need to be a flex or Grid container, and could be any block element instead. We would only need the <code>align-content</code> property on the parent to centre its children:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.parent</span> <span class="token punctuation">{</span>
<span class="token property">align-content</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>I assume the resulting behaviour would be similar to using <code>align-content</code> with Grid rather than flexbox.</p>
<p>As Rachel pointed out, this has actually been part of the spec for some time (although I certainly wasn’t aware of it!). It’s apparently in Chrome Canary (but not working for me at the time of writing!) and planned for general release in Chrome 122.</p>
<h2>Testing for browser support</h2>
<p>One thing that concerns me, is that this seems to fall into that tricky area where it becomes impossible to test for browser support and provide fallbacks using a feature query — much like <code>gap</code> when it was implemented for flexbox. As <code>align-content</code> is well-supported for Grid and flexbox, the feature query doesn’t help us here.</p>
<pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@supports</span> <span class="token punctuation">(</span><span class="token property">align-content</span><span class="token punctuation">:</span> center<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token comment">/* This will resolve true for any browsers supporting the property in grid or flexbox */</span>
<span class="token punctuation">}</span></code></pre>
<p>It would be great to see some improvements to how feature queries can handle these sorts of situations.</p>
Interview: Beyond the Spotlight2023-12-18T00:00:00Zhttps://css-irl.info/interview-beyond-the-spotlight/<figure>
<img src="https://css-irl.info/beyond-the-spotlight_1024.webp" alt="Profile pic with the text “Beyond the Spotlight”" width="1024" height="620" srcset="https://css-irl.info/beyond-the-spotlight_1024.webp 1024w, https://css-irl.info/beyond-the-spotlight_800.webp 800w" sizes="(min-width: 1086px) 75vw, (min-width: 1264px) 930px, 90vw" />
</figure>
<p>Melinda Seckington has just launched a brand new blog series, <strong>Beyond the Spotlight</strong>, where she’ll be talking to conference speakers about their talk process, preparation and delivery. Melinda is an accomplished speaker , writer and engineering manager, so I was honoured to be the <a href="https://www.seckington.com/beyond-the-spotlight-michelle-barker/">first speaker interviewed</a>.</p>
<p>It was interesting to reflect on my experience of speaking at conferences and consider what drew me into it. I’m looking forward to reading more in the series.</p>
<p><a href="https://www.seckington.com/beyond-the-spotlight-michelle-barker/">Read the interview</a></p>
Greenhouse Gas Emissions From Streaming Digital Content2023-12-11T00:00:00Zhttps://css-irl.info/greenhouse-gas-emissions-from-streaming-digital-content/<p>I’ve been speaking about web sustainability a bit recently, and a question that comes up fairly frequently is about the impact of audio and video streaming. Catherine Van Loo from <a href="https://carnstone.com/">Carnerstone</a> consultancy is part of the team working on <a href="https://dimpact.org/about">DIMPACT</a> — a collaborative initiative between researchers and digital media companies with the aim of quantifying and reducing carbon emissions from digital content. She recently gave an <a href="https://greentechsouthwest.org/insights/dimpact/">illuminating talk at Green Tech South West</a> on the subject.</p>
<p>Some key takeaways:</p>
<ul>
<li>Reducing emissions from digital streaming is a cross-sectoral challenge. It requires collaboration from streaming companies, the ICT sector, device manufacturers, and others who might be invested in reducing emissions, such as record labels and artists.</li>
<li>The vast majority of carbon emissions are generated in the use phase — from the user consuming content on their devices.</li>
<li>Streaming on a mobile network uses more energy than on wi-fi.</li>
<li>Size of device plays a part: a large desktop computer would consume more power than a mobile device for the same content.</li>
<li>The <strong>amount</strong> of data transferred doesn’t necessarily correlate to the amount of energy used on the network, as companies optimise for peak periods. But over time this would cause those peaks to become larger, and the companies to increase their network power and infrastructure to deal with these larger peaks.</li>
<li>The DIMPACT model doesn’t take into account the embodied emissions (the carbon emissions associated with manufacturing the devices and infrastructure), but these are still important to consider.</li>
</ul>
<p>I highly recommend watching the talk in full. There are also links to plenty of resources mentioned in the talk.</p>
<p><a href="https://greentechsouthwest.org/insights/dimpact/">Watch the talk</a></p>
<p><a href="https://ctprodstorageaccountp.blob.core.windows.net/prod-drupal-files/documents/resource/public/Carbon-impact-of-video-streaming.pdf">Read the Carbon Trust’s report on the impact of streaming (PDF)</a></p>
Reporting on Website Carbon Emissions2023-12-08T00:00:00Zhttps://css-irl.info/reporting-on-website-carbon-emissions/<p>Yesterday I spoke at <a href="https://smashingconf.com/meets-green">Smashing Meets Goes Green</a>, an online meetup themed around building a more sustainable web. In my talk I spoke a little about tools for measuring your website’s carbon emissions, and how it would be great if these metrics could be integrated with commonly-used developer tools for monitoring performance. As I also mentioned, it’s really hard to land on 100% accurate figures for this stuff, as there are so many variables.</p>
<p>Nevertheless, bringing these metrics into focus, however imperfect, would contribute in a big way towards awareness of the carbon overhead of of digital products, and hopefully encourage some designers and developers to build more sustainable websites.</p>
<p>Fershad Irani has written a <a href="https://calendar.perfplanet.com/2023/why-web-perf-tools-should-be-reporting-website-carbon-emissions">fascinating deep-dive</a> into the models used for calculating website carbon emissions, their limitations, and how they could be improved. He argues that despite their imperfections, we should be reporting on this stuff anyway.</p>
<p><a href="https://calendar.perfplanet.com/2023/why-web-perf-tools-should-be-reporting-website-carbon-emissions">Read the article</a></p>
Testing the Performance of Social Media Embeds2023-12-04T00:00:00Zhttps://css-irl.info/testing-the-performance-of-social-media-embeds/<p>I’ve been writing and speaking about web sustainability quite a bit this past year. One thing I’ve done periodically for my talks is to test the data transfer size of various social media embeds — they’re pretty bad!</p>
<p>I first tested some YouTube embeds a couple of years ago while attempting to improve the site performance of a client at the agency I worked for. At the time, embedding a YouTube video on a webpage caused ~600kb of JavaScript to be downloaded. Eek! It gets worse though. In May this year, embedding that same video resulted in more than 800kb of JS. Pretty disappointing that it had actually increased, and by a not-insignificant amount. Conducting the same test today results in downloading over 1.2MB of JS 😳 That’s pretty eye-watering. Peeking into the Network tab, 785kb seems to be a single base JS file.</p>
<h2>What’s up with the cache?</h2>
<p>These tests were all conducted with a fresh, uncached page. There seems to be some decent caching going on, as Chrome reports only 75kB of JS transferred on a return visit, as well as when visiting a page with a different embed. On Firefox however, the same page with the same embed reports 2.9MB of JS transferred! What is going on?! Are dev tools inaccurate, or are different browsers downloading resources differently?</p>
<p>After a bit of Googling, I discovered that Firefox introduced a feature called <a href="https://slides.com/valentingosu/race-cache-with-network-2017">RCWN</a> (Race Cache With Network) some time ago. This led me to this fascinating article by Simon Hearne, <a href="https://simonhearne.com/2020/network-faster-than-cache/">When Network is Faster Than Cache</a>. It turns out that sometimes retrieving a resource from the cache can take longer than fetching it from the network! In Firefox’s case, files are being requested from both the cache and the network, and the fastest one wins. On my home broadband connection, the network seems to beat the cache nearly every time, which (I assume) is why we’re seeing those files transferred each time. On slower connections this probably wouldn’t be the case.</p>
<p>It’s reassuring to know that users on poor connections would have those files served from the cache. But Simon’s article makes the point that cache retrieval can be slow, and becomes slower the more resources are requested from the cache, which particularly for those with low-powered devices and connections.</p>
<blockquote>
<p>There is an assumption that cached assets are retrieved instantly and at zero cost. What we have discovered here is that there is in fact a cost to retrieving assets from cache based on the number of cached assets (not file size) and the user's devices.</p>
</blockquote>
<p>Additionally, I’m not sure that hitting the network every time (in Firefox’s case) is more sustainable?! Google’s <a href="https://web.dev/articles/offline-cookbook#cache-and-network-race">explanatory article</a> on cache strategies notes that it can be ideal for small assets. It also notes:</p>
<blockquote>
<p>However, going to the network when the user has the content on their device can be a waste of data, so bear that in mind.</p>
</blockquote>
<p>Hmm. Surprisingly enough (or not at all surprisingly), the best route to good performance and a more sustainable website is to keep your pages as lightweight as possible, and avoid your users having to download all that data in the first place. In that case, it’s all the more disappointing to see Google increase the amount of JS shipped with the YouTube embed, perhaps in the mistaken believe that the cache will save the day.</p>
<p>I’m not a performance expert, so I might be missing a crucial piece of the puzzle. I’d love to hear from folks who have some more insight into this!</p>
<p>If you’d like to test the performance of a few different types of social media embed (including YouTube videos), I’ve made <a href="https://social-media-embed-test.netlify.app/">a lightweight website</a> to do just that.</p>
Wrapping Up National Blog Posting Month 20232023-11-30T00:00:00Zhttps://css-irl.info/wrapping-up-national-blog-posting-month-2023/<p>We made it! I tasked myself with writing 30 posts in 30 days for National Blog Posting Month, and somehow I just about managed it (well, including this post — yes, I cheated!). So, what have I learned?</p>
<h2>Let yourself off the hook</h2>
<p>Paradoxically, giving myself an off-ramp right at the beginning made me push myself harder. In the first post I had already set myself up with reasons (excuses) why I might not complete the task of posting every day for a month. I let myself feel comfortable with the idea that <strong>trying</strong> was the main thing, and that I wasn’t competing with anyone. Somehow that made me even more determined not to miss a day. I’m at my best when I’m only competing with myself.</p>
<h2>Anything can spark an idea</h2>
<p>That’s not to say it wasn’t hard at times. There were days when I definitely struggled to think of a topic worthy of posting about, or to put my ideas into words. But, as I wrote on <a href="https://css-irl.info/what-to-blog-about-when-you-dont-know-what-to-blog-about/">day 23</a>, a blog topic can be just about <strong>anything</strong>. I kept a list of interesting things I’d read or seen, and dipped into those on days when I wasn’t feeling very original.</p>
<p>I also gave myself permission to keep posts short and sweet, some of them more like journal entries with half-formed ideas. I created a new category for demos on my blog, which helps me feel more comfortable with the idea of posting a quick demo with minimal explanation. Previously I often felt the pressure to accompany those kind of code experiments with a full tutorial.</p>
<h2>Writing helps with thinking</h2>
<p>I’ve always got a lot out of the process of writing, but this is the first time I’ve really paid attention to how writing helps with the thinking process, sparking a chain of thoughts and ideas. Quite often I’d start a post thinking I would just write a few lines, but it ended up being much longer and the act of writing kicked off the thought process and helped clarify ideas.</p>
<h2>No room for perfection</h2>
<p>If I waited until all my posts were perfect before I published them, I’d never publish <strong>anything</strong>. It’s OK to make mistakes and go back and correct yourself later, or extend and add to posts. (It’s also fine to leave them well alone!) But publishing every day for a month meant I absolutely did <strong>not</strong> have time to let my perfectionist tendencies take over. Sometimes I had to grit my teeth and resign myself to publishing something that I knew wasn’t as polished as it could have been. This was one of the hardest parts.</p>
<h2>Write for yourself</h2>
<p>I love it when my writing benefits others, but first and foremost I write for myself. The process of writing helps embed coding tips and techniques and gives me a chance to explore new APIs and specifications outside of a work context, which gives me freedom to play and discover. And this blog provides me with a lasting trove of reference material from my past self that I can’t get any other way.</p>
<p>I haven’t been sharing many of these posts on social media, partly because I don’t want to spam people, and partly because many of them <strong>do</strong> feel like journal entries. I’m not sure if anyone would want to read them, and I’ve been mansplained a few too many times recently, which I don’t really have the energy to deal with. But writing without the pressure to share on social media and chase engagement has been quite freeing. It reminds me why I do it.</p>
<p>The discipline required to write every day for a month has helped me build a writing habit, and while I don’t plan to continue with it every day going forward, I do hope I’ll publish more regularly.</p>
<h2>That’s a wrap</h2>
<p>If you’ve been following along, then thank you! I hope you’ve enjoyed the ride, and maybe learned a thing or two, or sparked a few ideas of your own.</p>
<p>Here are all the posts, in order:</p>
<ol>
<li><a href="https://css-irl.info/national-blog-posting-month/">National Blog Posting Month</a></li>
<li><a href="https://css-irl.info/update-on-the-cop28-website/">Update on the COP28 Website</a></li>
<li><a href="https://css-irl.info/messing-about-with-css-gradients/">Messing About with CSS Gradients</a></li>
<li><a href="https://css-irl.info/code-gardening/">Code Gardening</a></li>
<li><a href="https://css-irl.info/owning-your-web/">Owning Your Web</a></li>
<li><a href="https://css-irl.info/leaving-twitter-behind/">Leaving Twitter Behind</a></li>
<li><a href="https://css-irl.info/dont-mind-the-gap/">(Don’t) Mind the Gap</a></li>
<li><a href="https://css-irl.info/handling-null-undefined-and-zero-values-in-javascript/">Handling Null, Undefined and Zero Values in JavaScript</a></li>
<li><a href="https://css-irl.info/nan-or-not-a-number/">NaN or Not a Number?</a></li>
<li><a href="https://css-irl.info/css-nesting-is-here/">CSS Nesting is Here</a></li>
<li><a href="https://css-irl.info/stop-using-ai-generated-images/">Stop Using AI-Generated Images</a></li>
<li><a href="https://css-irl.info/programming-as-a-craft/">Programming as a Craft</a></li>
<li><a href="https://css-irl.info/better-vue-application-state-management-with-vuex-modules/">Better Vue Application State Management with Vuex Modules</a></li>
<li><a href="https://css-irl.info/radial-gradients-and-css-trigonometric-functions/">Radial Gradients and CSS Trigonometric Functions</a></li>
<li><a href="https://css-irl.info/the-joy-of-lists/">The Joy of Lists</a></li>
<li><a href="https://css-irl.info/using-flow-diagrams-to-manage-state/">Using Flow Diagrams to Manage State in Complex Applications</a></li>
<li><a href="https://css-irl.info/thoughts-on-ux-engineering/">Thoughts on UX Engineering</a></li>
<li><a href="https://css-irl.info/drawing-raindrops-with-css-gradients-and-masks/">Drawing Raindrops with CSS Gradients and Masks</a></li>
<li><a href="https://css-irl.info/a-fun-css-text-effect/">A Fun CSS Text Effect</a></li>
<li><a href="https://css-irl.info/reality-check-a-series-for-building-real-layouts/">Reality Check: A Series for Building Real Layouts</a></li>
<li><a href="https://css-irl.info/scroll-timeline-parallax-effect/">Scroll Timeline Parallax Effect</a></li>
<li><a href="https://css-irl.info/choosing-a-green-web-host/">Choosing a Green Web Host</a></li>
<li><a href="https://css-irl.info/what-to-blog-about-when-you-dont-know-what-to-blog-about/">What to Blog About When You Don’t Know What to Blog About</a></li>
<li><a href="https://css-irl.info/why-you-should-hold-onto-your-devices-for-longer/">Why You Should Hold Onto Your Devices For Longer</a></li>
<li><a href="https://css-irl.info/preventing-overscroll-bounce-with-css/">Preventing Scroll “Bounce” with CSS</a></li>
<li><a href="https://css-irl.info/you-have-something-to-say-thats-worth-hearing/">You Have Something to Say That’s Worth Hearing</a></li>
<li><a href="https://css-irl.info/kicking-the-excessive-javascript-habit/">Kicking the Excessive JavaScript Habit</a></li>
<li><a href="https://css-irl.info/oh-no-overflow/">Oh No, Overflow!</a></li>
<li><a href="https://css-irl.info/hide-and-debug-empty-elements-with-css/">Hide and Debug Empty Elements with CSS</a></li>
<li>Wrapping Up National Blog Posting Month 2023</li>
</ol>
Hide and Debug Empty Elements with CSS2023-11-29T00:00:00Zhttps://css-irl.info/hide-and-debug-empty-elements-with-css/<p>A tiny tip today, but a good one: use the <code>:empty</code> pseudo-class to hide pesky empty elements (commonly found in user-generated content).</p>
<pre class="language-css"><code class="language-css"><span class="token selector">p:empty</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>This will hide paragraph elements that have <strong>no children</strong>, or contain <strong>no text nodes</strong>. Text nodes include whitespace, so a paragraph containing a whitespace character <strong>won’t</strong> be hidden in this case. On the other hand, if a content editor has pressed <kbd>Enter</kbd> a bunch of times, this’ll do a great job of hiding those extra generated paragraphs.</p>
<p><code>:empty</code> is also pretty handy for debugging. We can add a red outline to any empty element and see what might be causing us layout issues:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">:empty</span> <span class="token punctuation">{</span>
<span class="token property">outline</span><span class="token punctuation">:</span> 1px solid <span class="token property">red</span><span class="token punctuation">:</span>
<span class="token punctuation">}</span></code></pre>
<p>Grid layout are one place this can be useful. Sometimes it’s hard to understand why an element is placed on a particular grid column or row. It’s worth remembering that it could be down to empty elements!</p>
<p class="codepen" data-height="384" data-default-tab="css,result" data-slug-hash="qBgMmMb" data-user="michellebarker" style="height: 384px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/qBgMmMb">
Grid with empty cells</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>Be aware, elements that only have content determined by the CSS <code>content</code> property will still be considered empty. It’s not good practice to use CSS for most content anyway — always put important content in your HTML.</p>
Oh No, Overflow!2023-11-28T00:00:00Zhttps://css-irl.info/oh-no-overflow/<p>The <code>overflow</code> CSS property (shorthand for <code>overflow-x</code> and <code>overflow-y</code>) controls what happens when the stuff you put inside a box is bigger than the box itself. Should it bleed out of the box (<code>overflow: visible</code>, the default)? Hidden completely (<code>overflow: hidden</code>)? Or should the content be scrollable (<code>overflow: scroll</code> or <code>overflow: auto</code>)?</p>
<h2>Why can I see scroll bars (or not)?</h2>
<p>Both <code>scroll</code> and <code>auto</code> should clip the content to the element’s padding box. According to <a href="https://drafts.csswg.org/css-overflow/#values">the spec</a>, when an element uses <code>scroll</code> the user agent should display scroll bars when this value is set in one direction, whether or not any content is overflowing or clipped. Using <code>auto</code>, the scroll bars should only display when there <strong>is</strong> overflow — therefore they should not be visible if the content is not larger that the container box.</p>
<p>In practice however, this behaviour is inconsistent. On a Mac the scroll bars are only visible when the user hovers over the scrollable element. Whereas Windows often renders the scrollbars when <code>auto</code> is used, even if there is no overflow.</p>
<aside>
<p>While we’re talking about scroll bars, the article <a href="https://artemis.sh/2023/10/12/scrollbars.html">Scrollbars are becoming a problem</a> by Artemis Everfree is well worth a read.</p>
</aside>
<p>For the purpose of this article I’m going to talk a lot about width and height, inline and block axis. I’m making some assumptions in referring mostly to documents in the default horizontal <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/writing-mode">writing mode</a>, where width corresponds to the inline axis and height to the block axis. It adds another layer of complexity when we get to talking about logical properties, writing modes and direction, as I’m sure many of these rules will be flipped around! But as I’ve only developed web applications in horizontal writing modes, that’s where my expertise lies.</p>
<h2>Overflow is good</h2>
<p>Overflow is pretty useful: if our content is longer than the height of the viewport, users expect to be able to scroll to see it. We get that behaviour for free (thanks browsers!). If we set a fixed height on an element and our content is longer then we’ll get overflow. We rarely set fixed heights on the web, preferring (sensibly) to let the height be determined by the content, but an exception might be a layout like this, where we have a fixed, full-height menu on the left of our content. If there are more items in our menu than can be accommodated by the height of the element, the user should be able to scroll them into view. We can handle that easily by setting <code>max-height: 100vh</code> (or whatever our fixed height is) and <code>overflow-y: auto</code> on the left-hand element.</p>
<figure>
<img srcset="https://css-irl.info/overflow-01_1600.webp 1600w, https://css-irl.info/overflow-01_1200.webp 1200w" sizes="(min-width: 1200px) 1200px, 90vw" src="https://css-irl.info/overflow-01_1200.webp" width="1600" height="900" alt="UI with scrollable menu on the left" />
</figure>
<p>Keep that in your mind, because we’re going to come back to that layout later on!</p>
<h2>Uh oh, overflow!</h2>
<p>Sometimes we might need to deal with unexpected overflow causing layout bugs. Often that manifests itself as pesky scrollbars appearing where we don’t want them. Some things that commonly cause overflow:</p>
<h3>Images</h3>
<p>Unlike height, constraint by width is commonplace, as we’re limited by the width of the viewport itself. Block elements have a width of 100% by default, so they shouldn’t generally overflow unless <strong>a:</strong> we give them a fixed width or minimum width, or <strong>b:</strong> we put something in them that has intrinsic width, like an image with a width of 1600px in a 1400px viewport.</p>
<figure>
<img srcset="https://css-irl.info/overflow-02_1600.webp 1600w, https://css-irl.info/overflow-02_1200.webp 1200w" sizes="(min-width: 1200px) 1200px, 90vw" src="https://css-irl.info/overflow-02_1200.webp" width="1600" height="900" alt="Image wider than the available space, with scrollbar below" />
</figure>
<p>It’s common to add <code>max-width: 100%</code> to your CSS reset to prevent this.</p>
<h3>Long words</h3>
<p>I work on a web app for wind farm operators. Some of our clients are German, and sometimes a long German wind farm name will cause issues in a flex box or grid layout.</p>
<figure>
<img srcset="https://css-irl.info/overflow-03_1600.webp 1600w, https://css-irl.info/overflow-03_1200.webp 1200w" sizes="(min-width: 1200px) 1200px, 90vw" src="https://css-irl.info/overflow-03_1200.webp" width="1600" height="900" alt="A word longer than the widht of the menu, which is clipped" />
</figure>
<p>One way to solve that is with <code>overflow-wrap: break-word</code>, which will allow the text to wrap within a single word where there is insufficient space.</p>
<p>In some cases we might want to clip the text and use <code>text-overflow: ellipsis</code>. I’m not a huge fan of hiding content from users, but there are occasional cases that warrant it.</p>
<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="JjxajdE" data-user="michellebarker" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/JjxajdE">
text-overflow: ellipsis</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<h3>Transforms</h3>
<p>Sometimes we might hide an element off-screen with a transform, perhaps in order to animate it into view upon a chosen interaction. It’s easy to forget that the transformed element can still cause overflow!</p>
<figure>
<img srcset="https://css-irl.info/overflow-04_1600.webp 1600w, https://css-irl.info/overflow-04_1200.webp 1200w" sizes="(min-width: 1200px) 1200px, 90vw" src="https://css-irl.info/overflow-04_1200.webp" width="1600" height="900" alt="A square box with arrow to indicate it has been moved off-screen to the right, with scrollbar below" />
</figure>
<p>We <strong>could</strong> hide it with <code>display: none</code> in order to prevent this, which would cause the rest of the layout to behave as if the overflowing element isn’t there. Alternatively we could set <code>overflow: hidden</code> on a parent element.</p>
<figure>
<img srcset="https://css-irl.info/overflow-06_1600.webp 1600w, https://css-irl.info/overflow-06_1200.webp 1200w" sizes="(min-width: 1200px) 1200px, 90vw" src="https://css-irl.info/overflow-06_1200.webp" width="1600" height="900" alt="overflow: hidden on a container surrounding the transformed square element, scrollbar omitted to show the page is no longer scrollable" />
</figure>
<h3>Flexbox layouts</h3>
<p>Flexbox layouts are a common cause of unexpected overflow. Each element might on its own have no fixed size, but combined could be wider than the available space.</p>
<p>Sometimes the simple solution is <code>flex-wrap: wrap</code>, which might be a good solution for a horizontal menu, for example. Other times we might need to take a closer look at the intrinsic width of individual elements in our flex layout, or adjust the <code>flex-grow</code>, <code>flex-shrink</code> or <code>flex-basis</code> values.</p>
<h2>Delegating overflow</h2>
<p>Because overflow is <code>visible</code> by default (except on the root element), we’ll generally only get horizontal overflow on the root element if the overflowing element exceeds the viewport.</p>
<figure>
<img srcset="https://css-irl.info/overflow-07_1600.webp 1600w, https://css-irl.info/overflow-07_1200.webp 1200w" sizes="(min-width: 1200px) 1200px, 90vw" src="https://css-irl.info/overflow-07_1200.webp" width="1600" height="900" alt="horizontal navigation overflowing the viewport" />
</figure>
<p>By setting <code>overflow</code> to <code>scroll</code> or <code>auto</code> on an ancestor of the overflowing element we cause the horizontal scrolling behaviour to be removed on the root element (providing there isn’t another element causing it), and the ancestor to handle it instead. This could be another possible solution to the horizontal menu example above.</p>
<figure>
<img srcset="https://css-irl.info/overflow-08_1600.webp 1600w, https://css-irl.info/overflow-08_1200.webp 1200w" sizes="(min-width: 1200px) 1200px, 90vw" src="https://css-irl.info/overflow-08_1200.webp" width="1600" height="900" alt="horizontal navigation with overflow: auto instead" />
</figure>
<p>It’s also useful for creating a scrollable gallery, or a table.</p>
<h2>Debugging overflow</h2>
<p>An element causing horizontal overflow is a common bug, and one that can be tricky (and irritating) to debug. At one time you would get a warning about it if you run your site through Lighthouse, although I’m unsure if it’s still the case. My personal technique is to go through the DOM commenting out chunks of code until I find the area that’s causing the problem.</p>
<h3>Debugging with Firefox</h3>
<p>Firefox dev tools are really helpful when it comes to debugging overflow. If you inspect the DOM you’ll see a little “overflow” tag on the offending element, and a “scroll” tag on the scrollable ancestor. Handy!</p>
<h2>Problem: overflow visible on one axis</h2>
<p>So, unwanted overflow issues can be a bit of a pain, but it helps to understand what causes them and how to debug them. There are some quirks of the <code>overflow</code> property that are a little inconvenient, however.</p>
<p>Let’s go back to our previous example of a fixed sidebar menu. The menu might be longer than the visible height of its parent, so we’ll need <code>overflow-y: auto</code> to allow the user to scroll in order to discover all the menu items. The menu I built used some technical language that might be unfamiliar to new users, so I was asked to add some tooltips, which would display to the right of the hovered element.</p>
<figure>
<img srcset="https://css-irl.info/overflow-09_1600.webp 1600w, https://css-irl.info/overflow-09_1200.webp 1200w" sizes="(min-width: 1200px) 1200px, 90vw" src="https://css-irl.info/overflow-09_1200.webp" width="1600" height="900" alt="Menu with tooltips that display on hover" />
</figure>
<p>It would make sense to include these in the HTML close to the actual link they describe, in order to position it correctly. Then we could use CSS to position the tooltip to the right and display it on hover or focus:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.tooltip</span> <span class="token punctuation">{</span>
<span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span>
<span class="token property">top</span><span class="token punctuation">:</span> 50%<span class="token punctuation">;</span>
<span class="token property">left</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>
<span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">translateY</span><span class="token punctuation">(</span>-50%<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">a:hover + .tooltip,
a:focus + .tooltip</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> block<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Unfortunately, because the menu has overflow in one direction, hovering on a menu item (causing the tooltip to appear) will cause overflow in the horizontal direction too! What we really want is <code>overflow-x: visible</code> and <code>overflow-y: auto</code>. But sadly that combination isn’t permitted.</p>
<figure>
<img srcset="https://css-irl.info/overflow-10_1600.webp 1600w, https://css-irl.info/overflow-10_1200.webp 1200w" sizes="(min-width: 1200px) 1200px, 90vw" src="https://css-irl.info/overflow-10_1200.webp" width="1600" height="900" alt="Scrollable menu on the left, with a tooltip that would cause overflow" />
</figure>
<p>I’ve tried out a CSS-only solution using <code>:has()</code>, where the tooltips are positioned outside the menu:</p>
<p class="codepen" data-height="300" data-default-tab="result" data-slug-hash="KKbQMgd" data-user="michellebarker" data-token="109e3feb543a55112937b5e2a7a440ee" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/KKbQMgd/109e3feb543a55112937b5e2a7a440ee">
Overflow menu tooltips</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>There are plenty of <a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/tooltip_role">accessibility considerations</a> around tooltips though, so we might want to consider a JS solution.</p>
Kicking the Excessive JavaScript Habit2023-11-27T00:00:00Zhttps://css-irl.info/kicking-the-excessive-javascript-habit/<p>The video of Ryan Townsend’s talk from <a href="https://perfnow.nl/">Performance Now()</a>, <em>The Unbearable Weight of Massive JavaScript</em> is well worth a watch. Excessive JS, seemingly turbo-charged by JS frameworks, has long been a problem on the web, with bundle sizes showing no sign of decreasing.</p>
<p>https://www.youtube.com/watch?v=f5felHJiACE</p>
<p>As Ryan points out, all that JS is rarely in the service of users. Rather, architectural choices are made primarily for <strong>developer experience</strong>. It seems like few companies question whether they <strong>need</strong> a JS framework like React. Companies hire for React-and-Typescript, so developers learn those skills, and in turn they want to work with those frameworks because it’s what they know, so they push for those.</p>
<p>One slide in particular stayed with me:</p>
<blockquote>
<p>Trickle-down user experience</p>
</blockquote>
<p>Yep, that’s it. We convince ourselves that by prioritising the developer experience that <strong>we</strong> want, we’ll be able to provide a better experience for users. The problem is, it’s just not working out that way. Our stacks are fragile, breakable. The slightest breeze and they come crashing down. Trickle-down user experience works pretty much just as well as trickle-down economics. What users want are fast, accessible, useable websites. We’re just not giving them that.</p>
<p>We’re also making things pretty complicated for ourselves, tying ourselves (and future developers) into an ecosystem of dependency spaghetti, and bending over backwards to re-implement things in JS that the browser gives us for free.</p>
<h2>You can count on web standards</h2>
<p>It’s refreshing to see a renewed interest in web components in recent weeks — something I confess I still haven’t got around to learning properly. Rather than frameworks which come and go, and require regular love and attention in order to continue working, web standards are designed to be future-proof. And with new features like nesting and cascade layers, it’s becoming easier to write CSS at scale without requiring a build step.</p>
<p>I wouldn’t say there’s no place for JS frameworks, but they could definitely be used a lot less.</p>
<h2>HTML first...or is it?</h2>
<p>Relatedly, this <a href="https://html-first.com/">HTML First</a> manifesto(?) has been doing the rounds. On initial glance, I agree! HTML-first is what web standards people have been advocating for years, right?! And there’s some good ideas in there: Prefer “Vanilla” approaches over frameworks! Avoid build steps!</p>
<p>Where I’m not so sure I agree is on the idea that <strong>everything</strong> should go in the HTML. And some of the advice feels downright contradictory: the author advocates for libraries like <a href="https://tailwindcss.com/">Tailwind</a> and <a href="https://alpinejs.dev/">Alpine.js</a>, which <strong>do</strong> require a build step, at least if you want to ship them in an efficient way, and I’m not sure that using HTML attributes in the way advocated in the example is more understandable. (Side note: Alpine.js bugs me because you end up littering your HTML with invalid attributes.) I’m not die-hard “separation-of-concerns-at-all-costs”, but I thinks there are benefits to separating CSS and JS from HTML on the whole. I’m not convinced that (as the author claims) “it will be easier for other developers to find and understand behaviour, navigate it, and make changes to it”.</p>
<p>The author proclaims that you should be able to right-click to view source and be able to understand the underlying code. Lots of people would agree, but again, I feel that’s a little contradictory when you’re peppering a web page with inline styles and JS. But to be honest, I don’t really care about being able to view source. I know that’s something of an unpopular opinion, but I’ve never found it particularly helpful. We have much better developer tools than 10 years ago, it’s never been easier to debug in the browser. As long as the HTML shipped to the user is valid and accessible, I’m not bothered what looks like underneath. So I understand where people are coming from when they lament the good old “view source” days, but I feel like to build a workflow around that risks once again prioritising developers over users.</p>
<p>I don’t want to dismiss everything in the article though. I love the idea of simplifying how we build for the web, and weaning ourselves of our excessive JavaScript habit. It’ll be interesting to see if some of these ideas catch on.</p>
You Have Something to Say That’s Worth Hearing2023-11-26T00:00:00Zhttps://css-irl.info/you-have-something-to-say-thats-worth-hearing/<p>A recent post by Melinda Seckington, <a href="https://www.seckington.com/the-myth-of-not-having-anything-to-talk-about/">The Myth of not Having Anything To Talk About</a> really resonated with me. I’ve been doing talks about CSS for a few years now, and for a while before that I was an organiser of web conferences, so I’ve seen a fair few talks. I can definitely say that some of the best talks I’ve seen are not necessarily by the people with the most expertise in a given subject, but by people who share <strong>their</strong> story, <strong>their</strong> perspective — whether it’s on a particular technology, design problem, workflow, project case study, or something else entirely. Making it personal makes it relateable. As Melinda says:</p>
<blockquote>
<p>There are 8 billion people on this planet, we all have our interests and topics that we resonate with, and your specific combination of your interests, your background and your experiences are unique to you. That means you have a unique perspective - only you can tell your stories.</p>
</blockquote>
<p>(This advice can absolutely apply to blog posts too, by the way!)</p>
<p>Public speaking is not something that comes naturally to me. I’m glad I pushed myself to do it, as it’s opened so many doors, and I get a great sense of achievement from overcoming my internal barriers. But even though I’ve been public speaking for a while, I still get nervous, and I still get the feeling from time to time that no one will be interested in what I have to say. I’m going to bookmark this post to read whenever I feel that way.</p>
Preventing Scroll “Bounce” with CSS2023-11-25T00:00:00Zhttps://css-irl.info/preventing-overscroll-bounce-with-css/<p>When you scroll rapidly to the top or bottom of a webpage you might notice a “bounce” effect, where the browser momentarily allows you to scroll beyond the uppermost or lowermost point, before bouncing you to correct position.</p>
<p>A typical layout pattern I’ve built a few times uses grid to position a sidebar menu next to a scrollable content area. I use <code>position: sticky</code> so that the menu stays in view, while the main content area scrolls. Using <code>position: sticky</code> is handy for this, as it means the sidebar width can still be determined by the content, unlike with <code>position: fixed</code>.</p>
<p>Unfortunately this can result in an undesirable effect in some browsers when the user scrolls to the bottom — particularly if the sidebar has a different coloured background to the rest of the page, as the user will see a brief flash of the colour below below.</p>
<p>There’s a simple way to prevent this with css, using the <code>overscroll-behavior</code> property on the document root:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">:root</span> <span class="token punctuation">{</span>
<span class="token property">overscroll-behavior</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>By setting the value to <code>none</code>, the browser will no longer scroll beyond the top (or bottom) of the page, and your sidebar will remain fixed in place.</p>
<p class="codepen" data-height="452" data-default-tab="result" data-slug-hash="vYbrpbX" data-user="michellebarker" style="height: 452px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/vYbrpbX">
overscroll-behaviour</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
Why You Should Hold Onto Your Devices For Longer2023-11-24T00:00:00Zhttps://css-irl.info/why-you-should-hold-onto-your-devices-for-longer/<p>My iPhone 8 is still going, four years after I bought it. That shouldn’t be a great achievement, but somehow it is. The battery life isn’t great (it’s already been replaced once), some apps are noticeably slower, and Apple have announced that the iPhone 8 won’t support the iOS 17 update, which means I’ll have to seriously consider upgrading in the not-too-distant future. I’m hoping to make it to the five-year mark, but we’ll see.</p>
<p>On Black Friday, it feels like we’re constantly bombarded with the latest deals to tempt us into buying new tech that we otherwise wouldn’t consider. But hardware has an environmental cost, both in its manufacture and it’s disposal. There is the embodied carbon associated with the manufacturing process, the chemical pollution, the rare metals used in manufacture, and the hazards of e-waste disposal.</p>
<h2>Right to repair</h2>
<p>Apple products have been criticised for their built-in obsolescence, and Apple has been accused of making it difficult for non-Apple technicians to repair their products, meaning that users often feel they have to discard old devices after a few years and upgrade. This seems to be changing slowly, with many people who recognise the appalling quantity of e-waste these policies cause pushing for the <a href="https://repair.eu/">right to repair</a> — although critics argue that Apple’s commitment so far <a href="https://repair.eu/news/apples-self-repair-programme-is-not-the-right-to-repair-we-need/">doesn’t go far enough</a>. In the UK, the <a href="https://therestartproject.org/right-to-repair/">Restart Project</a>, campaigns for the right to repair.</p>
<h2>The e-waste problem</h2>
<p><a href="https://www.genevaenvironmentnetwork.org/resources/updates/the-growing-environmental-risks-of-e-waste/">E-waste</a> is defined as used electronic devices with batteries or plugs that are at the end of their useful life. This includes devices such as computers, smartphones and tablets. It is estimated that humans generate over 50 million tons of e-waste per year, only a small proportion of which is recycled. Much of this waste ends up in developing countries, where it poses an environmental and health hazard.</p>
<h3>Preventing e-waste</h3>
<p>The main thing you can do to prevent e-waste is to avoid upgrading unnecessarily and hold onto your devices for as long as possible. If a device is no longer needed, consider selling or donating it, or if it can no longer be used then take it to a recycling facility or return it to the manufacturer.</p>
<p>This Black Friday, please consider the environment and maybe <strong>don’t</strong> buy that new device.</p>
<h2>Learn more about greening the web</h2>
<p>Join myself and <a href="https://gerrymcgovern.com/">Gerry McGovern</a> on Thursday 7th December online for <a href="https://smashingconf.com/meets-green">Smashing Meets Goes Green</a> — a special, sustainability focused edition of the meetup!</p>
<figure>
<img src="https://css-irl.info/smashing-meets-goes-green.svg" width="800" height="600" alt="Smashing Meet Goes Green" />
</figure>
What to Blog About When You Don’t Know What to Blog About2023-11-23T00:00:00Zhttps://css-irl.info/what-to-blog-about-when-you-dont-know-what-to-blog-about/<ol>
<li>Here’s a cool thing I made.</li>
<li>Here’s a cool thing someone else made.</li>
<li>Here’s something I just learned.</li>
<li>Here’s something I want to learn that looks cool.</li>
<li>Why I want to learn/use/do this thing.</li>
<li>Why I don’t want to learn/use/do the thing.</li>
<li>I’m in the process of learning something but I haven’t quite got it figured out yet.</li>
<li>Here’s something else I discovered while learning a thing.</li>
<li>Let’s learn a thing together!</li>
<li>Here’s a tip, tool and/or person that helped me recently. Maybe it’ll help you too.</li>
<li>Here’s a problem that’s been bugging me.</li>
<li>This is what I think about work/life/the industry/the world/this UI component right now.</li>
<li>Here’s an interesting thing someone else has to say. Here’s my take on it.</li>
<li>I’m feeling motivated! This is what’s motivating me.</li>
<li>I’m feeling unmotivated or burnt out. Here’s why.</li>
<li>This is what I’ve been reading/watching/listening to recently.</li>
<li>Here’s a list of things I could blog about.</li>
</ol>
Choosing a Green Web Host2023-11-22T00:00:00Zhttps://css-irl.info/choosing-a-green-web-host/<p>Earlier this month, Jeremy Keith posed the question: <a href="https://adactio.com/journal/20646">“How green is my server?”</a>. As Jeremy notes, it’s surprisingly hard to get that information! So how do you ensure that you’re hosting your website on a green server?</p>
<h2>What do we mean by “green”?</h2>
<p>When we think of green web hosting, most of us might reasonably assume that a hosting company that claims to be green would source a high proportion of their electricity from renewable sources. But is that necessarily the case?</p>
<h3>Green energy</h3>
<p>Unless the data centre is directly hooked up to a solar farm or wind turbine (unlikely), then then probably don’t get <strong>all</strong> of their electricity from renewables. Most large web hosts have data centres all over the world, and these are usually connected to the country’s national power grid. The proportion of electricity generation can vary country to country, and fluctuate depending on the weather and the time of day. Therefore, one of the things to look for is whether the company in question has a contract with a green electricity supplier, as <a href="https://sustainability.aboutamazon.com/products-services/the-cloud?energyType=true#renewable-energy">Amazon claims to</a>:</p>
<blockquote>
<p>We contract for renewable power from utility scale wind and solar projects that add clean energy to the grid.</p>
</blockquote>
<p>Hosting companies generally don’t publish their electricity suppliers’ details on their websites, however, so investigating their claims is far from straightforward.</p>
<p>We should also be aware that some companies claiming to be entirely green or “100% renewable” might only be buying <a href="https://en.wikipedia.org/wiki/Carbon_offsets_and_credits">carbon credits or offsets</a> rather than sourcing clean energy. These are likely to be far less effective, and in some cases could be actively harmful. When choosing a green web host it’s important to check whether they actually invest in renewable energy.</p>
<h3>Beyond renewable energy</h3>
<p>Beyond green energy alone, data centres consume vast quantities of water in order to keep them cool. As noted in the <a href="https://www.washingtonpost.com/climate-environment/2023/04/25/data-centers-drought-water-use/">Washington Post</a>:</p>
<blockquote>
<p>Data centers rank among the top 10 water-consuming commercial industries in the United States.</p>
</blockquote>
<p>This is a big concern, particularly in areas of drought.</p>
<p>There is also the <a href="https://circularecology.com/carbon-footprint-v-embodied-carbon.html">embodied carbon</a> (CO2 emitted in producing materials) to consider when it comes to data centres and other web hosting infrastructure.</p>
<p>I’m not aware of many hosting companies that publish detailed sustainability information on anything beyond electricity use (if they even publish that).</p>
<h2>Renewable energy vs. energy efficiency</h2>
<p>There’s much more we could examine when it comes to the energy efficiency of our site’s infrastructure. <a href="https://www.wholegraindigital.com/blog/how-using-a-cdn-is-better-for-people-and-the-planet/">Using a CDN</a> (Content Delivery Network) <strong>could</strong> be a greener choice, as web pages can be served close to the the user, reducing data transfer. But with a more distributed network there could be a trade-off, as there may be less control over the carbon-intensity of the electricity.</p>
<p>Digital agency UsTwo published an interesting <a href="https://ustwo.com/blog/reducing-the-carbon-footprint-of-our-website/">case study</a> on how they reduced their website’s carbon footprint by effectively building their own distributed hosting network — but of course, that’s beyond the means of many people!</p>
<p>Utilising serverless architecture could be a positive step too, but it as with so much else on the web, <a href="https://dev.to/slsbytheodo/you-thought-serverless-was-green-here-is-how-to-really-make-it-sustainable-1m0p">it depends</a>.</p>
<h2>How green is my website?</h2>
<p>Running CSS { In Real Life } through <a href="https://www.websitecarbon.com/">Website Carbon</a> tells me that this website runs on clean energy. How can we be sure of that? Website Carbon has a section that explains <a href="https://www.websitecarbon.com/how-does-it-work/#green-energy">how the energy source is determined</a>, but notes that the data source does not distinguish between data centres that purchase green electricity and those that buy standard electricity but offset their emissions.</p>
<p>My website is hosted with Netlify, which I can learn from their <a href="https://www.netlify.com/sustainability/">sustainability page</a> uses AWS infrastructure. Amazon helpfully provides a webpage detailing their <a href="https://sustainability.aboutamazon.com/products-services">sustainability initiatives</a>. But we should remember that this is self-reported, and companies are inclined to put a positive spin on their environmental efforts for marketing purposes.</p>
<p>Although the energy infrastructure that powers my website <strong>appears</strong> to be relatively green, <a href="https://www.theverge.com/2022/8/1/23287351/amazon-climate-change-carbon-emissions-worse-2021">Amazon is hardly what I would consider a “green” company</a>, with a business that relies on unsustainable consumption, as well as ever-increasing demand for computing power and data infrastructure.</p>
<p>Even with reliable data, calculating carbon emissions from infrastructure is tricky, and it’s impossible to account for all variables. The creators behind Website Carbon have a page explaining <a href="https://sustainablewebdesign.org/calculating-digital-emissions/">how digital emissions are calculated</a> for their estimates, but it can only ever be a rough estimate.</p>
<h2>Finding a green web Host</h2>
<p>If you’re shopping for a green (or at least greener) web host, a good first port of call is the Green Web Foundation’s <a href="https://www.thegreenwebfoundation.org/tools/directory/">Green Hosting Directory</a>. Although there’s not a huge amount of information on each company (and individual companies’ websites can be frustratingly opaque), in order to be published in the directory companies need to provide proof of their green claims, so it’s a good baseline.</p>
<p>The team are currently working on a new and improved version of the directly that includes supporting evidence for providers.</p>
<h2>Learn more about greening the web</h2>
<p>On Thursday 7th December I’ll be speaking alongside <a href="https://gerrymcgovern.com/">Gerry McGovern</a> at <a href="https://smashingconf.com/meets-green">Smashing Meets Goes Green</a> — a special, sustainability focused edition of the meetup!</p>
<figure>
<img src="https://css-irl.info/smashing-meets-goes-green.svg" width="800" height="600" alt="Smashing Meet Goes Green" />
</figure>
<p><a href="https://smashingconf.com/meets-green">Join us online</a></p>
Scroll Timeline Parallax Effect2023-11-21T00:00:00Zhttps://css-irl.info/scroll-timeline-parallax-effect/<p>I’ve been playing around with <a href="https://drafts.csswg.org/scroll-animations-1">scroll-driven animations</a> a bit lately. Scroll timelines allow us to link the progress of element’s animation to the progress of a scroll container, as I wrote about not long ago for <a href="https://developer.mozilla.org/en-US/blog/scroll-progress-animations-in-css/">MDN</a>.</p>
<p>It’s remarkably easy to link an animation to the root scroller (which in most cases, is probably where it’s going to be most useful). This demo uses the <code>animation-timeline</code> property to create a parallax effect. We’re creating an anonymous scroll timeline linked to a keyframe animation. The animation translates an element on the <em>y</em> axis, using a custom property value. We’re using three elements positioned on top of each other as the background, foreground and middle ground layers. For each one all we have to do is adjust the custom property to change the amount that each layer moves, thereby changing whether they appear nearer or further away relative to the user. Pretty cool!</p>
<p>This demo will only work in Chromium for the moment.</p>
<p class="codepen" data-height="463.859375" data-default-tab="result" data-slug-hash="WNPzXew" data-user="michellebarker" style="height: 463.859375px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/WNPzXew">
Multi-layer parallax effect scroll timeline</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
Reality Check: A Series for Building Real Layouts2023-11-20T00:00:00Zhttps://css-irl.info/reality-check-a-series-for-building-real-layouts/<p><a href="https://set.studio/category/reality-check/">Reality Check</a> is a new article series from <a href="https://set.studio/">Set Studio</a> that focuses on CSS layout with real-world case studies. Each issue takes a design from Dribbble (or a similar platform) and demonstrates how to code it in CSS in a way that plays to the strengths of the medium, rather than aiming for pixel-perfect recreations. As Andy says in the first article:</p>
<blockquote>
<p>You have no idea what context your users will be visiting your site in and being the browser’s mentor, not it’s micromanager is a guaranteed way to build truly responsive front-ends that work for everyone.</p>
</blockquote>
<p>Andy advocates solid, HTML-first principles and makes use of his <a href="https://cube.fyi/">CUBE CSS</a> methodology. He does a great job of explaining every step of the process of coding, including <strong>why</strong> he advises building in a particular way, and any trade-offs and accessibility considerations that the designer might not have considered. You’ll find some great attention to detail, such as use of the <code>prefers-contrast</code> media query in the second edition, and why you might want to consider <code>align-items: baseline</code> rather than <code>center</code> in flexbox.</p>
<p>I love these type of posts, not only because they feature realistic examples, but because they contain plenty of lessons that will set you up for building <strong>any</strong> project, regardless of the design. I consider myself fairly competent when it comes to CSS layout, and I still find myself learning something new from people like Andy.</p>
<p>I highly recommend this series for anyone wanting to become more confident with CSS layout, and CSS in general.</p>
<p><a href="https://set.studio/category/reality-check/">Check out the series</a></p>
A Fun CSS Text Effect2023-11-19T00:00:00Zhttps://css-irl.info/a-fun-css-text-effect/<p>Today’s post is a quick one, but (hopefully!) a fun one! It’s a demo featuring a fun text effect, using <code>background-clip: text</code> and <code>text-stroke</code>. It’s not a new technique, but worth remembering.</p>
<p>It looks like the text has been clipped out of the black translucent background, but in fact the <code><h1></code> just has the same background as the body. I’m making sure they line up with each other by setting the <code>background-position</code> to <code>center</code>.</p>
<p class="codepen" data-height="400" data-default-tab="result" data-slug-hash="MWLQomX" data-user="michellebarker" style="height: 400px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/MWLQomX">
Background text fill</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>It’s interesting to see how browsers handle the different properties. <code>-webkit-text-stroke</code> (it has to be prefixed to work cross-browser) seems to render unevenly in Firefox with this particular font. I also think it looks pretty cool without the text stroke.</p>
<p>The other trick here is using the <code>filter</code> property to create the shadow. The text colour has to be set to <code>transparent</code> in order to see the background “through” the text, so <code>text-shadow</code> doesn’t work as it hides the text. I’m also using some of these techniques for the hero text effect on my <a href="https://michellebarker.co.uk/">personal site</a>.</p>
Drawing Raindrops with CSS Gradients and Masks2023-11-18T00:00:00Zhttps://css-irl.info/drawing-raindrops-with-css-gradients-and-masks/<figure>
<img src="https://css-irl.info/rainy-gradient_900.webp" width="1600" height="900" srcset="https://css-irl.info/rainy-gradient_1800.webp 1800w, https://css-irl.info/rainy-gradient_1600.webp 1600w, https://css-irl.info/rainy-gradient_1200.webp 1200w, https://css-irl.info/rainy-gradient_900.webp 900w" sizes="(max-width: 1080px) 90vw, 930px" alt="Blue raindrops on a light blue background" />
</figure>
<p>If you live in the UK like I do, you can’t escape the fact that it’s been a wet few months. Since today had been yet another rainy day, I was inspired to see if I could create a rainy background with CSS.</p>
<aside>
<p>After I created this demo, I belatedly discovered that it didn’t work as expected in Chrome. It turns out <code>mask-size</code> and <code>mask-repeat</code> (which the demo relies on) aren’t supported right now. I’ve updated the demos in this article to use the prefixed properties (<code>-webkit-mask-repeat</code> and <code>-webkit-mask-size</code>). Thanks to <a href="https://front-end.social/@css">Temani Afif</a> for pointing out the need for the prefix!</p>
</aside>
<p>I started of thinking I could get away with just using <code>background</code> properties. That enabled me to create the raindrop shape, but creating the highlights (which attempt to allude to reflections and refractions present in water droplets) was trickier. I decided to apply the raindrop shape as a mask, with the <code>mask-image</code> property. That mean the highlight (that I drew with <code>radial-gradient</code>) could bleed off the shape and wouldn’t be present in the background.</p>
<p>Using a mask meant I had to use a pseudo-element, as a mask on the <code><body></code> element won’t do anything. I’m using viewport units so that the raindrops will scale.</p>
<p class="codepen" data-height="455" data-default-tab="result" data-slug-hash="RwvQNgx" data-user="michellebarker" style="height: 414px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/RwvQNgx">
Rainy day gradient pattern (with viewport units)</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>I made a few more tweaks, cleaned up the code a little (with custom properties) and added a second pseudo-element, so the raindrops are staggered, which I think looks a lot better.</p>
<p>Here’s the result!</p>
<p class="codepen" data-height="455" data-default-tab="result" data-slug-hash="MWLQwpJ" data-user="michellebarker" style="height: 455px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/MWLQwpJ">
Staggered rainy day gradient pattern</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
Thoughts on UX Engineering2023-11-17T00:00:00Zhttps://css-irl.info/thoughts-on-ux-engineering/<p><a href="https://blog.damato.design/posts/terminal-career/">This post on the role of a UX Engineer</a> by Donnie D’Amato was published almost a year ago, but has cropped up in my feed today. UX engineering is something I identify strongly with, as I suspect do many of my friends on the <a href="https://bradfrost.com/blog/post/front-of-the-front-end-and-back-of-the-front-end-web-development/">front-of-the-front end</a>. Like Donnie, I’m from a design background, but cared a lot about <strong>how</strong> things were built, as well as how they looked and the function they performed for the user. I gradually made the transition to a more technical role over time, by dappling in front end development in my spare time. I’ve since gained a fair bit of experience in “back-of-the-front end” development, but “front-of-the-front end” is where my heart lies, and so the title of “UI and UX Engineer” is one that I can identify with.</p>
<p>I’m super lucky to have found a role at a company that values me as someone who sits on the bridge between design and front end. While I spend a lot of my time coding, including some fairly complex state management, I also get to contribute heavily to the product development and design. But I don’t take that for granted, and it hasn’t always been the case. In the past I’m aware I’ve been under-valued and underpaid compared to my more “technical” colleagues, despite the wealth of experience I was able to contribute that wasn’t explicitly in the engineering realm (read “soft skills”).</p>
<p>Like Donnie, I don’t associate with being a Software Engineer. Most of those kinds of jobs are pretty unappealing to me because they seem <strong>only</strong> concerned with code, whereas I see my role as encompassing a broader spectrum. There are people who are great at software engineering, and those are important jobs. But I can’t help but feel the tech industry might be better off if companies prioritised hiring and supporting UX Engineers too.</p>
<p><a href="https://blog.damato.design/posts/terminal-career/"><strong>Read the post:</strong> UX Engineer, a terminal career</a></p>
Using Flow Diagrams to Manage State in Complex Applications2023-11-16T00:00:00Zhttps://css-irl.info/using-flow-diagrams-to-manage-state/<p>A few days ago I blogged about managing state in Vue apps with <a href="https://css-irl.info/better-vue-application-state-management-with-vuex-modules/">Vuex modules</a>. I’m currently grappling with some fairly complex state management in a Vue app, and I wanted to share a bit about my process and how I’ve made it more manageable.</p>
<h2>Background</h2>
<p>I work on a web app for wind turbine operators to visualise data, plan maintenance and optimise the output of their turbines. It includes an area where operators can select turbines and compare data from various sensors across a selected date range, rendering different types of interactive visualisations. The app makes several different API calls to fetch data when the user updates their selection, and the page URL needs to update when different options are selected so that it can be shared.</p>
<p>This part of the app had already undergone several iterations, and was originally much simpler. Over time, new functionality had been bolted on. There was definitely some redundancy, and the code was becoming bloated and hard to understand. Therefore a big refactor was long overdue.</p>
<h2>Planning state management in Miro</h2>
<p>At this point, jumping straight into the code would be likely to lead me into an increasingly tangled mess of spaghetti. A better choice was to take a step back and look at the steps that need to be considered at each point in the user journey to make sure that could be reflected in the code.</p>
<p>I’m a visual person, and I’ve found the best way to break down complex coding tasks it with flow diagrams. <a href="https://miro.com/">Miro</a> is a useful tool for whiteboarding and collaborative work, which also has some great features for drawing flow diagrams. It also has a free tier. Of course, you can use whatever tool suits you!</p>
<h2>Mapping the user journey</h2>
<p>To get started, we could map out the user journey for someone landing on the data explorer page, updating some filters and clicking “Apply”, which triggers the API request.</p>
<figure>
<img src="https://css-irl.info/flow-chart-01_1600.webp" alt="Simple state management flow diagram" width="1600" height="900" />
</figure>
<p>This is already a good start. We can already begin to comprehend some of the functions we’ll need to write at each step. We could even begin to write them out in our state management library. This is an example of how we might write some actions in Vuex:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> actions <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token function-variable function">updateDateRange</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> state <span class="token punctuation">}</span><span class="token punctuation">,</span> value</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token function-variable function">updateTurbineSelection</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> state <span class="token punctuation">}</span><span class="token punctuation">,</span> value</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token function-variable function">updateChannelSelection</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> state <span class="token punctuation">}</span><span class="token punctuation">,</span> value</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token function-variable function">fetchData</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> state <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token function-variable function">updateVisualisations</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> state <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span></code></pre>
<p>In the flow diagram I’m using different colours for actions performed by the user (in pale blue here) and those initiated by the app in response (light purple). For example, the user clicks apply, but fetching the data is done by the app. In fact, and action is performed by the app in response to each user action. We could draw the flow diagram in a way that makes this much clearer.</p>
<figure>
<img src="https://css-irl.info/flow-chart-02_1600.webp" alt="State management flow diagram" width="1600" height="900" />
</figure>
<p>This is still overly simplistic. In reality there are many more steps we can (and should) consider. What happens if the user misses a step in the filter selection process and tries to click “Apply”? We’d probably want to prevent the API request until they’ve selected all the required options. Should we display a warning message? Disable the button until all options are selected? What happens if our API request fails? Should we inform the user?</p>
<p>We can start to flesh out the flow diagram further still. Now we can see some of the getter functions that we might need in our state management library.</p>
<figure>
<img src="https://css-irl.info/flow-chart-03_1600.webp" alt="State management flow diagram" width="1600" height="900" />
</figure>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> getters <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token function-variable function">shouldEnableApply</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter">state</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token function-variable function">getVisualisations</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter">state</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token function-variable function">getError</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter">state</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span></code></pre>
<h2>Planning our application state</h2>
<p>Once we’ve comprehensively plotted out the user journey, we can start translating this into how our application state will be structured. Below each of the steps we can add a text box showing the current state at each point. I like to highlight the newly updated parts, to clearly differentiate them.</p>
<figure>
<img src="https://css-irl.info/flow-chart-04_1600.webp" alt="State management flow diagram" width="1600" height="900" />
</figure>
<p>This helps us discern which parts of the state will need to be updated, and where we can reuse functions.</p>
<h2>Actions, getters and mutations</h2>
<p>Now we can think seriously about the functions we need to write to update the state and retrieve the computed values for use in our front end. By looking at the similarities we can see ways to avoid repetition and write code that’s more efficient.</p>
<p>Here we can see that the turbines and channels are updated in similar ways. Perhaps we just need one mutation to cover both cases. I’ll add these as labels below our state text boxes.</p>
<figure>
<img src="https://css-irl.info/flow-chart-05_1600.webp" alt="State management flow diagram" width="1600" height="900" />
</figure>
<p>Sometimes multiple things need to happen following a user’s interaction. For instance, if a user clicks a turbine to deselect it, we should remove it from the selection. We might want to group some mutations into actions.</p>
<figure>
<img src="https://css-irl.info/flow-chart-06_1600.webp" alt="State management flow diagram" width="1600" height="900" />
</figure>
<p>Here’s how we might write that in our JS code:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> mutations <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token function-variable function">addToFilterSelection</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter">state<span class="token punctuation">,</span> <span class="token punctuation">{</span> key<span class="token punctuation">,</span> value <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token function-variable function">removeFromFilterSelection</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter">state<span class="token punctuation">,</span> <span class="token punctuation">{</span> key<span class="token punctuation">,</span> value <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span></code></pre>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> actions <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token function-variable function">updateFilterSelection</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> state<span class="token punctuation">,</span> getters<span class="token punctuation">,</span> commit <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> key<span class="token punctuation">,</span> value <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> <span class="token punctuation">{</span> selectionIncludesOption <span class="token punctuation">}</span> <span class="token operator">=</span> getters
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">selectionIncludesOption</span><span class="token punctuation">(</span><span class="token punctuation">{</span> key<span class="token punctuation">,</span> value <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">commit</span><span class="token punctuation">(</span><span class="token string">'removeFromFilterSelection'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> key<span class="token punctuation">,</span> value <span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
<span class="token function">commit</span><span class="token punctuation">(</span><span class="token string">'addToFilterSelection'</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> key<span class="token punctuation">,</span> value <span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span></code></pre>
<h2>Summary</h2>
<p>Although we’ve vastly simplified the example, hopefully you can see how (if you’re a visual person) using tools like Miro to map out application state could help us write better code. It can also help when it comes to testing, ensuring we have full coverage. Let me know if you have any tips!</p>
Getting Started with Container Queries2023-11-16T00:00:00Zhttps://css-irl.info/getting-started-with-container-queries/<p>As of this year, container queries are supported in all major browsers. But what are they, and how can we use them to build more robust, flexible layouts? Do we still need media queries? Let's find out.</p>
<p>This post for MDN includes everything you need to get started using container queries for building layouts.</p>
<p><a class="o-hotlink" href="https://developer.mozilla.org/en-US/blog/getting-started-with-css-container-queries/" target="_blank">Read the post on MDN</a></p>
The Joy of Lists2023-11-15T00:00:00Zhttps://css-irl.info/the-joy-of-lists/<figure>
<img src="https://css-irl.info/the-joy-of-lists-01.svg" alt="Vector illustration of a notebook and pens with green background" />
</figure>
<p>I’m a big fan of lists. I like to think of myself as a fairly organised person. I make lists for a lot of stuff, at work and home, and get a little kick out of crossing things off. Yes, I’m one of those people who add tasks to a list post-completion, just for the pleasure of seeing it crossed off. This sounds completely pointless, but it’s actually a good motivator! I’m more motivated to tackle the rest of the list if I already have the sense of achievement that comes from already-completed tasks.</p>
<p>I’ve tried various attempts to move my lists to a digital format over the years, but I’ve always gone back to good old pen and paper. A couple of years ago I enthusiastically organised my life into Notion boards, only for things to, well, stop working. I’d open up the app on my phone and it would hang for ages, only for me to eventually forget whatever it was I was going to add to the list.</p>
<p>My lists aren’t pretty. I don’t have nice notebooks. I use scrap paper — old envelopes, school newsletters, the back of a child’s drawings begun and subsequently abandoned — folded and torn into small A6 rectangles, clipped together with a bulldog clip (a tip I learned from my mum). I tried bullet journaling for all of five minutes. Now I embrace the scrappiness, and relish the impermanence and total lack of perfection. And it means I can blue-tack them to any available surface. The fridge, calendar and kitchen cupboards are all peppered with lists.</p>
<p>I still keep a few digital lists too. My husband and I coordinate our shopping list through Alexa, and regularly shout things like “Alexa, add satsumas to the shopping list” across the room. That works great, until it doesn’t. We’ve learned from experience that the poor signal in our local supermarket means we frequently need to screenshot the list before leaving the house. Perhaps it's time to go back to pen and paper.</p>
<p>For more complex project coordination, spreadsheets serve me pretty well. I probably wouldn’t use them as the primary project management tool within an organisation, but for organising something like a list of blog topics (like for this series for #NaBloPoMo) they do the job and, crucially, can work offline. This means I don’t have to remember whichever app it was that I was using for a particular list.</p>
<p>That said, I still use Notion for some things, like organising my upcoming freelance work, and this blog. But lists don’t always need to be high tech, and I still write daily “to dos” despite the Notion board. There’s something about the physical act of writing that helps me remember. Digital can’t quite match that.</p>
Radial Gradients and CSS Trigonometric Functions2023-11-14T00:00:00Zhttps://css-irl.info/radial-gradients-and-css-trigonometric-functions/<figure>
<img src="https://css-irl.info/radial-gradient-trigonometry-01_900.webp" width="1600" height="900" srcset="https://css-irl.info/radial-gradient-trigonometry-01.webp 1600w, https://css-irl.info/radial-gradient-trigonometry-01_1200.webp 1200w, https://css-irl.info/radial-gradient-trigonometry-01_900.webp 900w" sizes="(max-width: 1080px) 90vw, 930px" alt="Flower shapes in black and white, made up of circles" />
</figure>
<p>I’ve been playing around with layering radial gradients in CSS to create flower shapes, with the help of CSS <a href="https://web.dev/articles/css-trig-functions">trigonometric functions</a>. For a primer on what trigonometric functions are, and why they’re useful in our code, I’ve written a <a href="https://tympanus.net/codrops/2021/06/01/trigonometry-in-css-and-javascript-introduction-to-trigonometry/">three-part series for Codrops</a>.</p>
<h2>Radial gradients</h2>
<p>We can draw circles as background images with the <code>radial-gradient()</code> function in CSS:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.item</span> <span class="token punctuation">{</span>
<span class="token property">background</span><span class="token punctuation">:</span> <span class="token function">radial-gradient</span><span class="token punctuation">(</span>
circle at 50% 50%<span class="token punctuation">,</span>
black 20%<span class="token punctuation">,</span>
transparent <span class="token function">calc</span><span class="token punctuation">(</span>20% + 1px<span class="token punctuation">)</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>This will give us a black circle in the centre of the element, with a radius of 20% of the element’s size. I’m using the <code>calc()</code> function here to smooth the edges of our gradient, <a href="https://medium.com/pixel-and-ink/avoiding-jagged-edges-on-gradients-f485cc7401f5">avoiding jagged lines</a>, which can be visible on some screens.</p>
<h2>Creating shapes</h2>
<p>We can layer multiple gradients to draw shapes.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.item</span> <span class="token punctuation">{</span>
<span class="token property">background</span><span class="token punctuation">:</span> <span class="token function">radial-gradient</span><span class="token punctuation">(</span>
circle at 50% 50%<span class="token punctuation">,</span>
black 20%<span class="token punctuation">,</span>
transparent <span class="token function">calc</span><span class="token punctuation">(</span>20% + 1px<span class="token punctuation">)</span>
<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">radial-gradient</span><span class="token punctuation">(</span>
circle at 60% 70%<span class="token punctuation">,</span>
black 25%<span class="token punctuation">,</span>
transparent <span class="token function">calc</span><span class="token punctuation">(</span>25% + 1px<span class="token punctuation">)</span>
<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">radial-gradient</span><span class="token punctuation">(</span>circle at 35% 35%<span class="token punctuation">,</span> black 15%<span class="token punctuation">,</span> transparent <span class="token function">calc</span><span class="token punctuation">(</span>15% + 1px<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>For these flower shapes, the idea is that we can use trigonometry to position the centre of each circle.</p>
<figure>
<img src="https://css-irl.info/radial-gradient-trigonometry.svg" alt="Six purple circles drawn around the circumference of a central circle outline" width="1600" height="900" />
</figure>
<p>If we imaging we’re placing each gradient circle on the circumference of a single circle, like a clock face, we need to know the angle of each point on the main circle. This will be 360 degree divided by the number of circles on our flower shape.</p>
<p>To make our code easier to read and reuse, we can use custom properties.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.item</span> <span class="token punctuation">{</span>
<span class="token property">--angle</span><span class="token punctuation">:</span> 360deg / <span class="token function">var</span><span class="token punctuation">(</span>--numberOfCircles<span class="token punctuation">,</span> 6<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>We need to work out the <em>x</em> and <em>y</em> position of each circle, and that’s where trigonometric functions come in. <em>x</em> is calculated by the <em>cosine</em> of the angle (the <code>cos()</code> function in CSS), multiplied by the radius — the distance from the centre of the point we’re calculating. <em>y</em> is calculated using <em>sine</em> (the <code>sin()</code> function in CSS):</p>
<pre class="language-css"><code class="language-css"><span class="token property">--x</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">cos</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--angle<span class="token punctuation">)</span><span class="token punctuation">)</span> * <span class="token function">var</span><span class="token punctuation">(</span>--radius<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">--y</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">sin</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--angle<span class="token punctuation">)</span><span class="token punctuation">)</span> * <span class="token function">var</span><span class="token punctuation">(</span>--radius<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Additionally, we need to shift our calculations to the centre of the element, otherwise all of our circles will sit off to the side. We’re going to move them along by 50%:</p>
<pre class="language-css"><code class="language-css"><span class="token property">--x</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span>50% + <span class="token function">cos</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--angle<span class="token punctuation">)</span><span class="token punctuation">)</span> * <span class="token function">var</span><span class="token punctuation">(</span>--radius<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">--y</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span>50% + <span class="token function">sin</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--angle<span class="token punctuation">)</span><span class="token punctuation">)</span> * <span class="token function">var</span><span class="token punctuation">(</span>--radius<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>That’ll place our drawn shape in the centre, but let’s instead use custom properties, in case we want to change the position later on.</p>
<pre class="language-css"><code class="language-css"><span class="token property">--x</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--posX<span class="token punctuation">)</span> + <span class="token function">cos</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--angle<span class="token punctuation">)</span><span class="token punctuation">)</span> * <span class="token function">var</span><span class="token punctuation">(</span>--radius<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">--y</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--posY<span class="token punctuation">)</span> + <span class="token function">sin</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--angle<span class="token punctuation">)</span><span class="token punctuation">)</span> * <span class="token function">var</span><span class="token punctuation">(</span>--radius<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>This calculates one of the points, but we need to do this multiple times — once for every circle we’re placing (or each of the petals of our flower). For six circles, we need six <em>x</em> and <em>y</em> variables, so let’s name them accordingly.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.item</span> <span class="token punctuation">{</span>
<span class="token property">--posX</span><span class="token punctuation">:</span> 50%<span class="token punctuation">;</span>
<span class="token property">--posY</span><span class="token punctuation">:</span> 50%<span class="token punctuation">;</span>
<span class="token property">--angle</span><span class="token punctuation">:</span> 360deg / <span class="token function">var</span><span class="token punctuation">(</span>--numberOfCircles<span class="token punctuation">,</span> 6<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">--radius</span><span class="token punctuation">:</span> 20%<span class="token punctuation">;</span>
<span class="token property">--a1</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--angle<span class="token punctuation">)</span> * 1<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">--x1</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--posX<span class="token punctuation">)</span> + <span class="token punctuation">(</span><span class="token function">cos</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--a1<span class="token punctuation">)</span><span class="token punctuation">)</span> * <span class="token function">var</span><span class="token punctuation">(</span>--radius<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">--y1</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--posY<span class="token punctuation">)</span> + <span class="token punctuation">(</span><span class="token function">sin</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--a1<span class="token punctuation">)</span><span class="token punctuation">)</span> * <span class="token function">var</span><span class="token punctuation">(</span>--radius<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">--a2</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--angle<span class="token punctuation">)</span> * 2<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">--x2</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--posX<span class="token punctuation">)</span> + <span class="token punctuation">(</span><span class="token function">cos</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--a2<span class="token punctuation">)</span><span class="token punctuation">)</span> * <span class="token function">var</span><span class="token punctuation">(</span>--radius<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">--y2</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--posY<span class="token punctuation">)</span> + <span class="token punctuation">(</span><span class="token function">sin</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--a2<span class="token punctuation">)</span><span class="token punctuation">)</span> * <span class="token function">var</span><span class="token punctuation">(</span>--radius<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">--a3</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--angle<span class="token punctuation">)</span> * 3<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">--x3</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--posX<span class="token punctuation">)</span> + <span class="token punctuation">(</span><span class="token function">cos</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--a3<span class="token punctuation">)</span><span class="token punctuation">)</span> * <span class="token function">var</span><span class="token punctuation">(</span>--radius<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">--y3</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--posY<span class="token punctuation">)</span> + <span class="token punctuation">(</span><span class="token function">sin</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--a3<span class="token punctuation">)</span><span class="token punctuation">)</span> * <span class="token function">var</span><span class="token punctuation">(</span>--radius<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">/* ...and so on */</span>
<span class="token punctuation">}</span></code></pre>
<aside>
<p>If we were using Sass, we could use a loop to help us avoid some of that repetition. But for this demo we’re keeping it vanilla, and there are no loops in regular CSS 🙂</p>
</aside>
<p>Now let’s plug these into some <code>radial-gradient()</code> functions. Again, we’ll use custom properties to help us avoid repetition, and so that we can change things easily later on.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.item</span> <span class="token punctuation">{</span>
<span class="token property">--size</span><span class="token punctuation">:</span> 20%<span class="token punctuation">;</span>
<span class="token property">--color</span><span class="token punctuation">:</span> black<span class="token punctuation">;</span>
<span class="token property">--grad</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--color<span class="token punctuation">)</span> <span class="token function">var</span><span class="token punctuation">(</span>--size<span class="token punctuation">)</span><span class="token punctuation">,</span> transparent <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--size<span class="token punctuation">)</span> + 1px<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">--baseGradient</span><span class="token punctuation">:</span> <span class="token function">radial-gradient</span><span class="token punctuation">(</span>circle at <span class="token function">var</span><span class="token punctuation">(</span>--x1<span class="token punctuation">)</span> <span class="token function">var</span><span class="token punctuation">(</span>--y1<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">var</span><span class="token punctuation">(</span>--grad<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token function">radial-gradient</span><span class="token punctuation">(</span>circle at <span class="token function">var</span><span class="token punctuation">(</span>--x2<span class="token punctuation">)</span> <span class="token function">var</span><span class="token punctuation">(</span>--y2<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">var</span><span class="token punctuation">(</span>--grad<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token function">radial-gradient</span><span class="token punctuation">(</span>circle at <span class="token function">var</span><span class="token punctuation">(</span>--x3<span class="token punctuation">)</span> <span class="token function">var</span><span class="token punctuation">(</span>--y3<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">var</span><span class="token punctuation">(</span>--grad<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">/* ...and so on, all the way up to 6 */</span><span class="token punctuation">;</span>
<span class="token property">background</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--baseGradient<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>In the final demo, I’m adding another circle at the centre.</p>
<h2>More petals</h2>
<p>If we want to add more circles (or petals to our flower shape) we need to make sure we define the additional variables we need (<code>--x7</code>, <code>--y7</code>, <code>--x8</code>, <code>--y8</code>, etc.) and add them to our background. Because we’ve defined the original <code>--baseGradient</code> variable for the background, we don’t need to write the whole thing out again. We can just append the new values, and update the custom property for the number of circles:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.item:nth-child(2)</span> <span class="token punctuation">{</span>
<span class="token property">--noOfCircles</span><span class="token punctuation">:</span> 7<span class="token punctuation">;</span>
<span class="token property">background</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--baseGradient<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">radial-gradient</span><span class="token punctuation">(</span>circle at <span class="token function">var</span><span class="token punctuation">(</span>--x7<span class="token punctuation">)</span> <span class="token function">var</span><span class="token punctuation">(</span>--y7<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">var</span><span class="token punctuation">(</span>--grad<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>In the demo, you can see I’m also changing the variables for the size (of the small circles) and radius (distance of each circle from the centre) for different effects.</p>
<p class="codepen" data-height="480" data-default-tab="result" data-slug-hash="RwvZExm" data-user="michellebarker" style="height: 480px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/RwvZExm">
Radial gradients with trigonometric functions</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<h2>What else can we do?</h2>
<p>There are lots of creative ways we can use these gradient effects, such as combining with <code>mix-blend-mode</code> for image overlays:</p>
<p class="codepen" data-height="518" data-default-tab="result" data-slug-hash="oNmemgr" data-user="michellebarker" style="height: 518px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/oNmemgr">
Radial gradients with trigonometric functions + mix-blend-mode</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>Or using the gradients as image masks:</p>
<p class="codepen" data-height="433" data-default-tab="result" data-slug-hash="NWovoda" data-user="michellebarker" style="height: 433px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/NWovoda">
Radial gradients with trigonometric functions + mask</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<aside>
<p>Note, these demos rely on CSS nesting, a relatively new feature.</p>
</aside>
<p>We could even animate them using <code>@property</code>, although that won’t work in Firefox yet. I’m sure there are plenty more creative possibilities!</p>
Better Vue Application State Management with Vuex Modules2023-11-13T00:00:00Zhttps://css-irl.info/better-vue-application-state-management-with-vuex-modules/<figure>
<img src="https://css-irl.info/better-vuex-state-management.svg" alt="Vector illustration of modules represented as a network of colourful squares" width="1600" height="900" />
</figure>
<p>If you use Vue you might be familiar with the state management library <a href="https://vuex.vuejs.org/">Vuex</a>. It used to be the state management library recommended by Vue, until the team developed <a href="https://pinia.vuejs.org/">Pinia</a>. But lots of applications still use Vuex, including the one I work on. This post contains some tips for handling state in a Vue app with Vuex. Some familiarity with building with Vue and Vuex will be assumed.</p>
<h2>The Vuex Store</h2>
<p>Using Vuex allows us to manage our application’s state in a global store, which helps avoid “prop drilling” — the process of passing down props through multiple levels of components. Using Vuex we can reach into the store and retrieve part of our state to use in a variable, or mutate the state from inside the component. This example uses Vue’s composition API:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">import</span> <span class="token punctuation">{</span> computed <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'vue'</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> useStore <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'vuex'</span>
<span class="token keyword">const</span> store <span class="token operator">=</span> <span class="token function">useStore</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">/* Retrieve the user from the store */</span>
<span class="token keyword">const</span> name <span class="token operator">=</span> <span class="token function">computed</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>currentUser<span class="token punctuation">.</span>name<span class="token punctuation">)</span></code></pre>
<p>Vuex lets us use getters, actions and mutations to better handle this. If <code>currentUser</code> is deeply nested within our application’s store, it might be better to abstract that out to a getter function in a separate JS file, avoiding polluting our component file with too much logic.</p>
<pre class="language-js"><code class="language-js"><span class="token comment">/* store/index.js */</span>
<span class="token keyword">const</span> store <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token literal-property property">getters</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token function-variable function">getUsername</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter">state</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>currentUser<span class="token punctuation">.</span>name
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span></code></pre>
<pre class="language-js"><code class="language-js"><span class="token comment">/* Component file */</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> computed <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'vue'</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> useStore <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'vuex'</span>
<span class="token keyword">const</span> store <span class="token operator">=</span> <span class="token function">useStore</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token comment">/* Retrieve the user from the store */</span>
<span class="token keyword">const</span> name <span class="token operator">=</span> <span class="token function">computed</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> store<span class="token punctuation">.</span>getters<span class="token punctuation">.</span>getUsername<span class="token punctuation">)</span></code></pre>
<p>For large applications, our Vuex store can get out of hand. In the project I’ve been working on, one highly interactive area of the app needed extensive state management. The Vuex store got pretty large, and became difficult to manage. We also ran into the problem of naming things. In such a large store, it was easy to inadvertently end up writing two functions with the same name. At this point, it made sense to split our Vuex store into modules.</p>
<h2>Creating modules</h2>
<p>As the name suggests, Vuex modules enable us to split our Vuex store into smaller files. This makes them easier to manage and test, and can help us avoid naming clashes by namespacing the module files.</p>
<p>We don’t have to refactor our entire app to use modules. If we have one particular part of the state that’s getting unwieldy, we can separate it out into its own module, leaving the rest of the application code as it was.</p>
<p>Previously, our store structure might look something like this:</p>
<pre class="language-bash"><code class="language-bash">store
<span class="token operator">|</span> index.js
<span class="token operator">|</span> actions.js
<span class="token operator">|</span> getters.js
<span class="token operator">|</span> mutations.js</code></pre>
<p>Lets add a <em>modules</em> directory, where we can house the getters, actions and mutations associated with the part of the app we’re concerned with (the data explorer section):</p>
<pre class="language-bash"><code class="language-bash">store
<span class="token operator">|</span> index.js
<span class="token operator">|</span> actions.js
<span class="token operator">|</span> getters.js
<span class="token operator">|</span> mutations.js
<span class="token operator">|</span> modules
<span class="token operator">|</span> <span class="token operator">|</span> dataExplorer
<span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> index.js
<span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> actions.js
<span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> getters.js
<span class="token operator">|</span> <span class="token operator">|</span> <span class="token operator">|</span> mutations.js</code></pre>
<p>We’ll create our new module in <em>store/modules/dataExplorer/index.js</em>. To keep things better organised, I prefer to import the getters, actions and mutations from their individual files, but you can write them in a single file if you choose.</p>
<p>We’re adding <code>namespaced: true</code> to ensure our module is namespaced, otherwise actions, mutations and getters are all registered under the global namespace by default.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">import</span> getters <span class="token keyword">from</span> <span class="token string">'./getters'</span>
<span class="token keyword">import</span> mutations <span class="token keyword">from</span> <span class="token string">'./mutations'</span>
<span class="token keyword">import</span> actions <span class="token keyword">from</span> <span class="token string">'./actions'</span>
<span class="token keyword">const</span> dataExplorerStore <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token literal-property property">namespaced</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
<span class="token function-variable function">state</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span>
getters<span class="token punctuation">,</span>
mutations<span class="token punctuation">,</span>
actions<span class="token punctuation">,</span>
<span class="token punctuation">}</span>
<span class="token keyword">export</span> <span class="token keyword">default</span> dataExplorerStore</code></pre>
<h2>Registering a module</h2>
<p>We’ll need to register the module with the store.</p>
<pre class="language-js"><code class="language-js"><span class="token comment">/* store/index.js */</span>
<span class="token keyword">import</span> getters <span class="token keyword">from</span> <span class="token string">'./getters'</span>
<span class="token keyword">import</span> mutations <span class="token keyword">from</span> <span class="token string">'./mutations'</span>
<span class="token keyword">import</span> actions <span class="token keyword">from</span> <span class="token string">'./actions'</span>
<span class="token keyword">import</span> dataExplorer <span class="token keyword">from</span> <span class="token string">'./modules/dataExplorer/index.js'</span>
<span class="token keyword">const</span> store <span class="token operator">=</span> <span class="token function">createStore</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token function-variable function">state</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token literal-property property">modules</span><span class="token operator">:</span> <span class="token punctuation">{</span>
dataExplorer<span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
getters<span class="token punctuation">,</span>
mutations<span class="token punctuation">,</span>
actions<span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<p>It’s also possible to register a module dynamically:</p>
<pre class="language-js"><code class="language-js">store<span class="token punctuation">.</span><span class="token function">registerModule</span><span class="token punctuation">(</span><span class="token string">'dataExplorer'</span><span class="token punctuation">,</span> dataExplorer<span class="token punctuation">)</span></code></pre>
<p>Our module will now be in the application state. We can access it in our component files (such as in a computed property):</p>
<pre class="language-js"><code class="language-js"><span class="token comment">/* Component file */</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> computed <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'vue'</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> useStore <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'vuex'</span>
<span class="token keyword">const</span> store <span class="token operator">=</span> <span class="token function">useStore</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token keyword">const</span> selectedDates <span class="token operator">=</span> <span class="token function">computed</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> store<span class="token punctuation">.</span>state<span class="token punctuation">.</span>dataExplorer<span class="token punctuation">.</span>startDate<span class="token punctuation">)</span></code></pre>
<h2>Local and global state</h2>
<p>In our module, <code>state</code> refers to the module’s local state. For example, when writing a getter function, <code>state</code> here would be <code>state.dataExplorer</code> in the global state object:</p>
<pre class="language-js"><code class="language-js"><span class="token comment">/* store/dataExplorer/getters.js */</span>
<span class="token keyword">const</span> getters <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token function-variable function">getStartDate</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter">state</span><span class="token punctuation">)</span> <span class="token operator">=></span> state<span class="token punctuation">.</span>startDate<span class="token punctuation">,</span>
<span class="token punctuation">}</span></code></pre>
<p>We can use the namespaces getter function in our component file:</p>
<pre class="language-js"><code class="language-js"><span class="token comment">/* Component file */</span>
<span class="token keyword">const</span> selectedDates <span class="token operator">=</span> <span class="token function">computed</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> store<span class="token punctuation">.</span>getters<span class="token punctuation">[</span><span class="token string">'dataExplorer/getStartDate'</span><span class="token punctuation">]</span><span class="token punctuation">)</span></code></pre>
<p>If we need to access the root state within a getter, we can use the third and fourth arguments of our getters:</p>
<pre class="language-js"><code class="language-js"><span class="token comment">/* store/dataExplorer/getters.js */</span>
<span class="token keyword">const</span> getters <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token comment">/* Gets startDate from the local state */</span>
<span class="token function-variable function">getStartDate</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter">state</span><span class="token punctuation">)</span> <span class="token operator">=></span> state<span class="token punctuation">.</span>startDate<span class="token punctuation">,</span>
<span class="token comment">/* Gets startDate from the global state */</span>
<span class="token function-variable function">getGlobalStartDate</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter">state<span class="token punctuation">,</span> getters<span class="token punctuation">,</span> rootState<span class="token punctuation">,</span> rootGetters</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> rootState<span class="token punctuation">.</span>startDate
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span></code></pre>
<h3>Actions</h3>
<p>Root getters can be accessed by our actions too:</p>
<pre class="language-js"><code class="language-js"><span class="token comment">/* store/dataExplorer/actions.js */</span>
<span class="token keyword">const</span> actions <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token function-variable function">updateStartDate</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> dispatch<span class="token punctuation">,</span> commit<span class="token punctuation">,</span> rootGetters <span class="token punctuation">}</span></span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> startDateGlobal <span class="token operator">=</span> rootGetters<span class="token punctuation">.</span>getStartDate
<span class="token function">commit</span><span class="token punctuation">(</span><span class="token string">'updateDate'</span><span class="token punctuation">,</span> startDateGlobal<span class="token punctuation">)</span> <span class="token comment">// update local state with the date from the global state</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span></code></pre>
<p>We can also dispatch root actions from our module:</p>
<pre class="language-js"><code class="language-js"><span class="token comment">/* store/dataExplorer/actions.js */</span>
<span class="token keyword">const</span> actions <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token function-variable function">updateStartDate</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token punctuation">{</span> dispatch<span class="token punctuation">,</span> commit <span class="token punctuation">}</span><span class="token punctuation">,</span> args</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token function">dispatch</span><span class="token punctuation">(</span><span class="token string">'notifyUser'</span><span class="token punctuation">,</span> args<span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token literal-property property">root</span><span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">)</span> <span class="token comment">// dispatch global action</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span></code></pre>
<p>To dispatch a local action from a component, we use it like this:</p>
<pre class="language-js"><code class="language-js"><span class="token comment">/* Component file */</span>
<span class="token function">onBeforeMount</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
store<span class="token punctuation">.</span><span class="token function">dispatch</span><span class="token punctuation">(</span><span class="token string">'dataExplorer/updateStartDate'</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<h2>Getting organised</h2>
<p>Modules are a handy way to manage state in large applications. It’s even possible to <a href="https://vuex.vuejs.org/guide/modules.html#namespacing">nest modules</a>, and to choose whether they inherit the parent namespace.</p>
<p>It’s possible to implement modules incrementally, avoiding an application-wide refactor. So if your global state is getting out of control, it might be worth giving modules a go today.</p>
Programming as a Craft2023-11-12T00:00:00Zhttps://css-irl.info/programming-as-a-craft/<p>Last week I had the pleasure of attending <a href="https://ffconf.org/">FF Conf</a> in Brighton. I was partly inspired to go to the conference after watching a talk from last year’s event: <a href="https://ffconf.org/talks/2022_lily_2point0_talk/">Programming With Yarn</a> by Lily Madar.</p>
<p>https://youtu.be/mcbg_mCQOHs?si=odn5zq9M6lx_xE1Z</p>
<p>Watching this talk coincided with a visit to my local museum in Trowbridge, which has an exhibition on the weaving industry that was prevalent in the area many years ago. Wandering around the exhibition, seeing the old Jacquard looms and the original punch cards used to “program” them (one of the early examples of computing) I was struck by the similarities between what we think of as programming today and other crafts. Lily’s talk touches on this in the context of the history of programming, as well as the similarities that knitting and crochet share with programming as we know it.</p>
<p>My own introduction to programming was teaching myself to program the lighting desk at the concert venue where I worked. It had a GUI, but the principles of variables, functions, loops and state were all applicable. I didn’t think of it as “programming” until much later when I became a web developer, but that’s absolutely what it was.</p>
<p>Nowadays, programming is often thought of as an elite skill that only a few have an aptitude for. Many creative people I know dismiss the idea that they could learn to code. But I’m convinced there are many transferable skills from other crafts and creative industries that would be an asset to any programmer. We aren’t so special.</p>
Stop Using AI-Generated Images2023-11-11T00:00:00Zhttps://css-irl.info/stop-using-ai-generated-images/<p>Recently I’ve been doing some front end development work for a lovely project that showcases original short stories submitted by writers and climate change activists. Most of the stories are accompanied by an illustration specially created by an artist. One story did not have an accompanying image, so the author suggested using an illustration produced by an AI image generator.</p>
<p>No doubt you won’t have failed to notice how AI (artificial intelligence) is proliferating in almost every area of our lives. At <a href="https://ffconf.org/">FF Conf</a> yesterday I spoke with at least 4 people (myself included) who work for companies that specialise in AI. There has been a lot of discussion (and soul-searching) about how LLMs (Large Language Models) like Chat-GPT will impact the internet, with many warning of the dangers of rampant misinformation. Plenty of writers are rightly worried about their livelihoods. The Hollywood writers’ strike is, in part, triggered by the concerns of writers about how AI will affect their jobs. <a href="https://maggieappleton.com/">Maggie Appleton</a> explored the topic in depth in her talk at FF Conference, and what some possible futures might look like.</p>
<p>AI image generators are seemingly of less concern. As we can see in the example at the beginning of this article, many authors who are concerned about AI subsuming their own work have few qualms about using AI image generators, which threaten to displace illustrators in the same way. I don’t believe this is conscious, for the most part. I think it’s likely that they view the creative process and output of illustrators differently from their own, where they consider it at all.</p>
<h2>How AI image generators are destroying illustrators’ livelihoods</h2>
<p>As a former illustrator, I confess to having some skin in the game here, and perhaps have a little more insight than most into the potential impact of AI on the industry. Just like LLMs, AI image generators are trained on image data scraped from the internet, much of it copyrighted, and almost all of it without the artists’ permission. Artists train for years, sometimes decades, to hone their craft in a precarious and low-paid industry. Without their skill, which produces the training data, these models simply wouldn’t be possible. And yet the artists are not compensated or credited in any way.</p>
<p>A common way for illustrators to break into the industry is through editorial commissions that might pay a couple of hundred pounds for a few days’ work, if they’re lucky. Illustrators need to work fast if they want to earn a decent living. Highly detailed, rich, photorealistic illustrations like the ones frequently produced by image generators can’t realistically be produced by most human illustrators in a short space of time, and so most editorial commission tend to feature styles that can be produced quickly, which tend towards simple and blocky. (Make no mistake though, communicating complex concepts in a simple, minimal style is a skill in itself.)</p>
<p>By choosing AI-generated images over those made by a human, editors are taking away one of the only sources of income available to those entering the field. This means that fewer people will choose to train in the profession. It will disproportionately affect those from low-income backgrounds, unable to justify the high cost of an art education with little prospect of earning a living. Illustration will become the preserve of the already well-off.</p>
<h2>Harmful images</h2>
<p>AI image generators suffer from a similar set of problems as LLMs like Chat-GPT, in that they can fill the internet with false information, so that you can no longer believe what you see. Add to that the ability to generate deepfakes and even child pornography (which there is already evidence they are being used for).</p>
<p>They are also accused of <a href="https://algorithmwatch.org/en/image-generators-stereotypes-diversity/">perpetuating bias</a>. As <a href="https://www.aiweirdness.com/">Janelle Shane</a> notes:</p>
<blockquote>
<p>AI tends to sand away the unusual</p>
</blockquote>
<p>Ben Myers explores this from his perspective in his insightful article <a href="https://benmyers.dev/blog/spotless-giraffe/">I’m a Spotless Giraffe</a>.</p>
<h2>Removing the human</h2>
<p>When I look at a piece of art, I’m looking at all the ways the human experience has shaped what I’m seeing. I’m admiring the skill, sure, but I’m also seeing the invisible human hand, the mind that has made the creative leaps. I feel connected to the artist, because they are telling me something about themselves.</p>
<p>AI art, by comparison, has no soul. AI can mimic. It can look impressive. It can’t feel the depth of human emotion, or distill the experience of being alive in a particular time and place into something more than the sum of its parts. I have yet to meet anyone who wants to hang AI art on their walls (although I fully expect to see it in hotel chains).</p>
<h2>AI art will eat itself</h2>
<p>It’s pretty easy to spot AI-generated art at the moment. A lot of it has an eerie, hyper-real aesthetic. Many details in the background or foreground can appear hazy and dream-like. Often you can more or less guess the exact prompt that was fed into the model in order to generate the image.</p>
<p>Perhaps AI will get better at mimicking actual human creativity. The flip-side is that as the web becomes awash with these kind of images, more models will be trained on them. The AI starts to consume its own output. I’m not sure what that future looks like.</p>
<h2>Artists are resisting</h2>
<p>The problems with AI image generators are explored in greater detail in a fascinating episode of the podcast <a href="https://www.techwontsave.us/episode/174_why_ai_is_a_threat_to_artists_w_molly_crabapple">Tech Won’t Save Us</a>, featuring illustrator Molly Crabapple.</p>
<p>The law hasn’t yet caught up, but artists are finding ways to resist. There have been lawsuits filed against AI companies, including stock photography company <a href="https://www.theverge.com/2023/2/6/23587393/ai-art-copyright-lawsuit-getty-images-stable-diffusion">Getty Images against Stable Diffusion</a>. A new tool has been developed that <a href="https://www.smithsonianmag.com/smart-news/this-tool-uses-poison-to-help-artists-protect-their-work-from-ai-scraping-180983183/">invisibly alters</a> how images will be interpreted by AI models when fed in as training data.</p>
<h2>We have a choice</h2>
<p>AI can be used by artists to augment their work and explore an exciting, new, creative frontier. But the vast majority of “AI art” I’ve seen is not that. It’s theft. You don’t have a right to “free” art, in the same way you don’t have a right to have your car fixed for free.</p>
<p>The proliferation of AI content on the web may feel all but inevitable, and perhaps it is. But we can choose whether to participate in that harm or not. We can ask ourselves, “Is this the web we want”?</p>
CSS Nesting is Here2023-11-10T00:00:00Zhttps://css-irl.info/css-nesting-is-here/<p>In case you missed it, <a href="https://www.w3.org/TR/css-nesting-1/">nesting</a> is now supported natively in CSS in all major browsers! Nesting is a popular feature of preprocessors like <a href="https://sass-lang.com/">Sass</a> and has been, in my opinion, on of the main reasons to keep using preprocessors. But with nesting supported natively, it might soon be time to consider dropping a preprocessor altogether.</p>
<h2>Nested selectors</h2>
<p>If you’re familiar with preprocessors then making the leap to native CSS nesting won’t be too difficult. Certainly the syntax is very similar to Sass. Consider the following markup:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Child level 1<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Child level 2<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>We can style the nested <code><div></code> (a child of the first-level <code><div></code>) by literally nesting the selector. The <code>&</code> operator is optional, but it should be noted that omitting this for element selectors is not currently supported in Chromium, although <strong>it’s coming in <a href="https://developer.chrome.com/blog/css-nesting-relaxed-syntax-update">Chrome 120</a></strong>.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">div</span> <span class="token punctuation">{</span>
<span class="token property">background</span><span class="token punctuation">:</span> lightblue<span class="token punctuation">;</span>
<span class="token comment">/* With the '&' operator */</span>
<span class="token selector">& div</span> <span class="token punctuation">{</span>
<span class="token property">background</span><span class="token punctuation">:</span> pink<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">/* Without the '&' operator */</span>
<span class="token selector">div</span> <span class="token punctuation">{</span>
<span class="token property">background</span><span class="token punctuation">:</span> pink<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>This is the same as writing:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">div</span> <span class="token punctuation">{</span>
<span class="token property">background</span><span class="token punctuation">:</span> lightblue<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">div div</span> <span class="token punctuation">{</span>
<span class="token property">background</span><span class="token punctuation">:</span> pink<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h2>Nesting combinators</h2>
<p>We can also use nesting for combinators. In this example (using the adjacent sibling combinator), a <code><div></code> that directly follows another <code><div></code> will have a lavender background. A <code><p></code> that is a direct child of a <code><div></code> will have a background of lemonchiffon.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">div</span> <span class="token punctuation">{</span>
<span class="token property">background</span><span class="token punctuation">:</span> lightblue<span class="token punctuation">;</span>
<span class="token selector">& + div</span> <span class="token punctuation">{</span>
<span class="token property">background</span><span class="token punctuation">:</span> lavender<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">& > p</span> <span class="token punctuation">{</span>
<span class="token property">background</span><span class="token punctuation">:</span> lemonchiffon<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>Again, this can be written with or without the <code>&</code>.</p>
<p>This is the same as writing:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">div</span> <span class="token punctuation">{</span>
<span class="token property">background</span><span class="token punctuation">:</span> lightblue<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">div + div</span> <span class="token punctuation">{</span>
<span class="token property">background</span><span class="token punctuation">:</span> lavender<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">div > p</span> <span class="token punctuation">{</span>
<span class="token property">background</span><span class="token punctuation">:</span> lemonchiffon<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h2>Compound selectors</h2>
<p>In the case of compound selectors, the <code>&</code> is necessary. In this example, a <code><div></code> with a class of <code>.featured</code> will have a turquoise background.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">div</span> <span class="token punctuation">{</span>
<span class="token property">background</span><span class="token punctuation">:</span> lightblue<span class="token punctuation">;</span>
<span class="token selector">&.featured</span> <span class="token punctuation">{</span>
<span class="token property">background</span><span class="token punctuation">:</span> turquoise<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<h2>Reversing the context</h2>
<p>Similarly to Sass, appending the <code>&</code> after the selector reverses the context. Here, if a <code><div></code> is a child of an element with a class of <code>featured</code> the <code><div></code> will have an orange background.</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>featured<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Child level 1<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>Child level 2<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<pre class="language-css"><code class="language-css"><span class="token selector">div</span> <span class="token punctuation">{</span>
<span class="token selector">.featured &</span> <span class="token punctuation">{</span>
<span class="token property">background</span><span class="token punctuation">:</span> orange<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<h2>Differences with Sass</h2>
<p>Unlike in Sass, selectors cannot be concatenated. This is not possible with native CSS nesting:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.featured</span> <span class="token punctuation">{</span>
<span class="token selector">&--large</span> <span class="token punctuation">{</span>
<span class="token property">font-size</span><span class="token punctuation">:</span> 1.4rem<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token comment">/* Equivalent to: */</span>
<span class="token selector">.featured--large</span> <span class="token punctuation">{</span>
<span class="token property">font-size</span><span class="token punctuation">:</span> 1.4rem<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>As we’ve seen above, we can combine selectors. This does increate the specificity though.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.featured</span> <span class="token punctuation">{</span>
<span class="token selector">&.featured--large</span> <span class="token punctuation">{</span>
<span class="token property">font-size</span><span class="token punctuation">:</span> 1.4rem<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token comment">/* Equivalent to: */</span>
<span class="token selector">.featured.featured--large</span> <span class="token punctuation">{</span>
<span class="token property">font-size</span><span class="token punctuation">:</span> 1.4rem<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h2>Nesting media queries</h2>
<p>We can nest media queries, container queries and other <code>@-</code>rules too:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">div</span> <span class="token punctuation">{</span>
<span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">min-width</span><span class="token punctuation">:</span> 600px<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<h2>Why nesting?</h2>
<p>Nesting can help make our code more readable and maintainable. I find it particularly helpful to be able to nest media queries. But it can go too far. A common rule advocated by many developers is to avoid nesting more than three levels deep.</p>
<p>There could be some performance concerns too. Using a preprocessor, the selectors are already parsed and compiled to a “flat” syntax before your code is sent to the browser. If the browser needs to parse the nested selectors, it could involve a bit more work. When used sensibly, I can’t imagine the performance impact is all that huge though.</p>
<h2>Older browsers</h2>
<p>CSS Nesting is still fairly new, so it’s possible it won’t be supported for a lot of users, who may be on older browsers. If you want to start using it, there is a <a href="https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-nesting">PostCSS polyfill</a>, which it would probably be wise to consider for now.</p>
NaN or Not a Number?2023-11-09T00:00:00Zhttps://css-irl.info/nan-or-not-a-number/<p>Following yesterday’s post on <a href="https://css-irl.info/handling-null-undefined-and-zero-values-in-javascript/">handling null, undefined and zero values in JS</a>, I was asked on <a href="https://mastodon.social/@noleli/111376820699911290">Mastodon</a>:</p>
<blockquote>
<p>I’m curious why <code>isNaN()</code> doesn’t work in your case. Thanks!</p>
</blockquote>
<p><code>isNan()</code> is a global function, which determines if a value evaluates to <code>NaN</code> or not. But what <strong>is</strong> <code>NaN</code>?</p>
<h2>NaN</h2>
<p><code>NaN</code> (not to be confused with your grandma) in JavaScript stands for <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NaN">Not A Number</a>. It’s relatively common for <code>NaN</code> to appear in JS code as a result of an impossible math operation, which can occur when passing a value of the wrong type into a function. For example:</p>
<pre class="language-js"><code class="language-js">console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token number">1</span> <span class="token operator">/</span> <span class="token keyword">undefined</span><span class="token punctuation">)</span> <span class="token comment">// NaN</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token number">1</span> <span class="token operator">/</span> <span class="token string">'abc'</span><span class="token punctuation">)</span> <span class="token comment">// NaN</span></code></pre>
<p><code>NaN</code> is a mathematical expression (rather than something that comes from JavaScript), and so, confusingly can also be thought of as a number! We can see that it <strong>is</strong> actually a type of number:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> value <span class="token operator">=</span> <span class="token number">1</span> <span class="token operator">/</span> <span class="token string">'abc'</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>value<span class="token punctuation">)</span> <span class="token comment">// NaN</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token keyword">typeof</span> value<span class="token punctuation">)</span> <span class="token comment">// 'number'</span></code></pre>
<h2>isNaN()</h2>
<p>We can use <code>isNaN()</code> to test whether a value evaluates to <code>NaN</code>. But it can be surprising. <code>'1'</code>, <code>'abc'</code>, <code>''</code> (an empty string), <code>null</code> and <code>'002'</code> are all clearly <strong>not</strong> numbers. But if we pass them into <code>isNaN()</code>, only two of them evaluate true:</p>
<pre class="language-js"><code class="language-js">console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token function">isNaN</span><span class="token punctuation">(</span><span class="token string">'1'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// false</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token function">isNaN</span><span class="token punctuation">(</span><span class="token string">''</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// false</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token function">isNaN</span><span class="token punctuation">(</span><span class="token string">'abc'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// true</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token function">isNaN</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// false</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token function">isNaN</span><span class="token punctuation">(</span><span class="token keyword">undefined</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// true</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token function">isNaN</span><span class="token punctuation">(</span><span class="token string">'002'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// false</span></code></pre>
<p><code>isNan()</code> attempts first to convert the value to a number. <code>null</code> or an empty string are coerced to <code>0</code>, for instance, meaning they can be parsed as numbers. Of these examples, only <code>'abc'</code> and <code>undefined</code> cannot be parsed as a numbers.</p>
<p>On the other hand, we can use the more robust <code>Number.isNaN()</code>, which does not first attempt to convert the value to a number:</p>
<pre class="language-js"><code class="language-js">console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>Number<span class="token punctuation">.</span><span class="token function">isNaN</span><span class="token punctuation">(</span><span class="token string">'1'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// false</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>Number<span class="token punctuation">.</span><span class="token function">isNaN</span><span class="token punctuation">(</span><span class="token string">''</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// false</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>Number<span class="token punctuation">.</span><span class="token function">isNaN</span><span class="token punctuation">(</span><span class="token string">'abc'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// false</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>Number<span class="token punctuation">.</span><span class="token function">isNaN</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// false</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>Number<span class="token punctuation">.</span><span class="token function">isNaN</span><span class="token punctuation">(</span><span class="token keyword">undefined</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// false</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>Number<span class="token punctuation">.</span><span class="token function">isNaN</span><span class="token punctuation">(</span><span class="token string">'002'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// false</span></code></pre>
<p>In this case, all of the expressions evaluate false.</p>
<h2>Filtering data</h2>
<p>Going back to yesterday’s example of filtering an array to remove <code>null</code> or <code>undefined</code> values, we can see that <code>isNaN()</code> (or <code>Number.isNaN()</code>) won’t cut it.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> data <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">100</span><span class="token punctuation">,</span> <span class="token number">52</span><span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token number">12</span><span class="token punctuation">,</span> <span class="token number">71</span><span class="token punctuation">,</span> <span class="token string">''</span><span class="token punctuation">,</span> <span class="token keyword">undefined</span><span class="token punctuation">]</span>
data<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">value</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token operator">!</span><span class="token function">isNaN</span><span class="token punctuation">(</span>value<span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token comment">// Results in [100, 52, null, 12, 71, '']</span></code></pre>
<pre class="language-js"><code class="language-js">data<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">value</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token operator">!</span>Number<span class="token punctuation">.</span><span class="token function">isNaN</span><span class="token punctuation">(</span>value<span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token comment">// Results in [100, 52, null, 12, 71, '', undefined]</span></code></pre>
<p>These filter functions don’t determine whether a value is a number, only whether a value is <strong>not</strong> Not A Number (<code>NaN</code>)! Therefore non-number values won’t all be filtered out.</p>
<p>To truly ensure that our filtered data only includes numbers, we can check if the type of each value is <code>'number'</code>:</p>
<pre class="language-js"><code class="language-js">data<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">value</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token keyword">typeof</span> value <span class="token operator">===</span> <span class="token string">'number'</span><span class="token punctuation">)</span></code></pre>
<p>However, as we’ve seen above, <code>NaN</code> is a type of number! So <code>NaN</code> values won’t be filtered out here.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> value <span class="token operator">=</span> <span class="token number">1</span> <span class="token operator">/</span> <span class="token string">'abc'</span>
<span class="token keyword">const</span> data <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token number">100</span><span class="token punctuation">,</span> <span class="token number">52</span><span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token number">12</span><span class="token punctuation">,</span> <span class="token number">71</span><span class="token punctuation">,</span> <span class="token string">''</span><span class="token punctuation">,</span> <span class="token keyword">undefined</span><span class="token punctuation">,</span> value<span class="token punctuation">]</span>
data<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">value</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token keyword">typeof</span> value <span class="token operator">===</span> <span class="token string">'number'</span><span class="token punctuation">)</span>
<span class="token comment">// Results in: [100, 52, 12, 71, NaN]</span></code></pre>
<p>To make our function even more robust, let’s use <code>isNaN()</code> to cover those too:</p>
<pre class="language-js"><code class="language-js">data<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">value</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token keyword">typeof</span> value <span class="token operator">===</span> <span class="token string">'number'</span> <span class="token operator">&&</span> <span class="token operator">!</span><span class="token function">isNaN</span><span class="token punctuation">(</span>value<span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token comment">// Results in: [100, 52, 12, 71] ☺️</span></code></pre>
Handling Null, Undefined and Zero Values in JavaScript2023-11-08T00:00:00Zhttps://css-irl.info/handling-null-undefined-and-zero-values-in-javascript/<p>In JS, it’s easy to get caught out when determining if a variable is null, undefined, or has a value of zero. I do a lot of data visualisation, and quite often I’ll need to filter out null values from an array of data.</p>
<pre class="language-js"><code class="language-js">data<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">value</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token operator">!</span><span class="token operator">!</span>value<span class="token punctuation">)</span></code></pre>
<p>The problem is, this is also going to filter out values of <code>0</code> too, because is tests whether the value is <a href="https://developer.mozilla.org/en-US/docs/Glossary/Falsy">falsy</a>. (Other falsy values include empty strings, and <code>NaN</code>.)</p>
<p>A better way might for this particular case might be to filter out non-numerical values instead:</p>
<pre class="language-js"><code class="language-js">data<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">value</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token keyword">typeof</span> value <span class="token operator">===</span> <span class="token string">'number'</span><span class="token punctuation">)</span></code></pre>
<p>Logical operators can be handy for providing default values. Using the <code>||</code> (<strong>or</strong>) operator, the <code>message</code> text will read “Unknown days since last post” if the <code>daysSinceLastPost</code> value doesn’t exist.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> message <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>daysSinceLastPost <span class="token operator">||</span> <span class="token string">'Unknown'</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> days since last post</span><span class="token template-punctuation string">`</span></span></code></pre>
<p>The issue here is that if <code>daysSinceLastPost</code> is <code>0</code>, the text will read “Unknown days since last post” too! Instead, we could use the <code>??</code> operator, otherwise known as the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing">nullish coalescing operator</a>.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> message <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>daysSinceLastPost <span class="token operator">??</span> <span class="token string">'Unknown'</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> days since last post</span><span class="token template-punctuation string">`</span></span></code></pre>
<p>That way, <code>daysSinceLastPost</code> will be used, even if the value is <code>0</code>, while our fallback will be used if it is <code>null</code> or <code>undefined</code>.</p>
<p>Put even more simply: In the following example, <code>test1</code> will evaluate to <code>'Test 1'</code>, while <code>test2</code> will evaluate to<code>0</code>. Nice!</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> test1 <span class="token operator">=</span> <span class="token number">0</span> <span class="token operator">||</span> <span class="token string">'Test 1'</span>
<span class="token keyword">const</span> test2 <span class="token operator">=</span> <span class="token number">0</span> <span class="token operator">??</span> <span class="token string">'Test 2'</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>test1<span class="token punctuation">)</span> <span class="token comment">// 'Test 1'</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>test2<span class="token punctuation">)</span> <span class="token comment">// 0</span></code></pre>
(Don’t) Mind the Gap2023-11-07T00:00:00Zhttps://css-irl.info/dont-mind-the-gap/<figure>
<img src="https://css-irl.info/gap-01_900.webp" width="1600" height="900" srcset="https://css-irl.info/gap-01_1600.webp 1600w, https://css-irl.info/gap-01_1200.webp 1200w, https://css-irl.info/gap-01_900.webp 900w" sizes="(max-width: 1080px) 90vw, 930px" alt="Vector illustration of a mobile UI with breadcrumbs" />
</figure>
<p>I don’t see people using the <code>gap</code> property for flexbox out in the wild all that often, but it’s pretty cool!</p>
<p><code>gap</code> has been supported for flexbox in all modern browsers since 2021, but has been supported for Grid for much longer. Grid originally had a <code>grid-gap</code> property (shorthand for <code>grid-row-gap</code> and <code>grid-column-gap</code>), but it was later changed to <code>gap</code> to make it compatible with flexbox. That presents a little bit of a problem when it comes to detecting support. In the following code, the styles inside the feature query would apply in browsers that support <code>gap</code> for Grid, even if they didn’t support it for flexbox.</p>
<pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@supports</span> <span class="token punctuation">(</span><span class="token property">gap</span><span class="token punctuation">:</span> 1rem<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token selector">.flex-layout</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span>
<span class="token property">gap</span><span class="token punctuation">:</span> 1rem<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>However, flexbox gap support is now so widespread that in some cases the feature query might no longer be needed.</p>
<h2>Usage</h2>
<p>One place I typically like to use <code>gap</code> is for breadcrumbs and similar layouts.</p>
<figure>
<img src="https://css-irl.info/gap-02_900.webp" width="1600" height="430" srcset="https://css-irl.info/gap-02_1600.webp 1600w, https://css-irl.info/gap-02_1200.webp 1200w, https://css-irl.info/gap-02_900.webp 900w" sizes="(max-width: 1080px) 90vw, 930px" alt="Typical linear breadcrumb layout with text in blue on white background" />
</figure>
<p>I use the flexbox for the layout, with <code>flex-wrap: wrap</code> to ensure that at small viewport sizes they’ll wrap onto a second row. We might use margins or padding on the right of the items to create some space between them.</p>
<figure>
<img src="https://css-irl.info/gap-03_900.webp" width="1600" height="634" srcset="https://css-irl.info/gap-03_1600.webp 1600w, https://css-irl.info/gap-03_1200.webp 1200w, https://css-irl.info/gap-03_900.webp 900w" sizes="(max-width: 1080px) 90vw, 930px" alt="Showing the margins between items, with items wrapped onto two lines" />
</figure>
<p>When they wrap onto the next line we can see that we’ll need some vertical space too, otherwise our breadcrumbs don’t look too great stacked on top of each other. So we end up adding a bottom margin to each item, as we don’t know at what point they’ll wrap (assuming our content could be of varying length).</p>
<figure>
<img src="https://css-irl.info/gap-04_900.webp" width="1600" height="634" srcset="https://css-irl.info/gap-04_1600.webp 1600w, https://css-irl.info/gap-04_1200.webp 1200w, https://css-irl.info/gap-04_900.webp 900w" sizes="(max-width: 1080px) 90vw, 930px" alt="The same layout with gap instead of margins" />
</figure>
<p>Unfortunately, that leaves us with some extra space at the bottom of our breadcrumb component, which we have to account for in the rest of the layout. (Sure, we could do <code>.item:not(las-child)</code>. But we don’t know if there will be more than one item on the last line.) We’ll also have some extra space to the right of each of our items.</p>
<p>The thing is, <code>gap</code> is pretty clever. Even though flexbox layouts are technically one dimensional (we can only control the row <strong>or</strong> column axis, not both), <code>gap</code> works across both axes.
If we opt instead to use <code>gap</code> instead of margins of padding, we only get space <strong>between</strong> the items.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.breadcrumbs-list</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span>
<span class="token property">flex-wrap</span><span class="token punctuation">:</span> wrap<span class="token punctuation">;</span>
<span class="token property">gap</span><span class="token punctuation">:</span> 1rem<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>And because it’s a shorthand (for <code>row-gap</code> and <code>column-gap</code>), we can set two different values if we choose:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.breadcrumbs-list</span> <span class="token punctuation">{</span>
<span class="token property">gap</span><span class="token punctuation">:</span> 0.5rem 1rem<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Then we don’t need to faff about with margins ☺️</p>
<figure>
<img src="https://css-irl.info/gap-05_900.webp" width="1600" height="634" srcset="https://css-irl.info/gap-05_1600.webp 1600w, https://css-irl.info/gap-05_1200.webp 1200w, https://css-irl.info/gap-05_900.webp 900w" sizes="(max-width: 1080px) 90vw, 930px" alt="Typical linear breadcrumb layout with text in blue on white background" />
</figure>
Leaving Twitter Behind2023-11-06T00:00:00Zhttps://css-irl.info/leaving-twitter-behind/<p>It’s been a year since Twitter (X) went down the pan, and I finally got around to removing the Twitter link from this site. I haven’t visited the platform in months, except periodically, guiltily checking that I haven’t missed an important DM. I never post there, and the occasional fleeting glimpse of my feed is enough to convince me I’m not missing much.</p>
<p>I think it’s pretty safe to say I’m not going back. The only reason I haven’t deleted my two accounts on there is because there are a bunch of places on the web that link to my Twitter profiles, and I don’t want someone else taking my username and causing confusion. Nor do I like the idea of leaving dead links out there in the wild, going nowhere.</p>
<p>I post on <a href="https://front-end.social/@michelle">Mastodon</a> semi-regularly, but overall I’ve cut down my social media use quite a bit, which I think is a good thing. Overall, what I’ve gained outweighs what I’ve lost.</p>
<p>Even posting on Mastodon sometimes I find it overwhelming to try and keep up with the replies, and the cognitive load of trying to have constructive discussions on there. I’m trying to share a bit more on LinkedIn, which seems wise from a career perspective. But quite often now I’ll post on this blog without sharing it anywhere. It’s my own cosy little corner of the web.</p>
<p>I’m grateful for the connections I made on Twitter. But I’m ready to stop chasing likes.</p>
Owning Your Web2023-11-05T00:00:00Zhttps://css-irl.info/owning-your-web/<figure>
<img src="https://css-irl.info/owning-your-web-01_900.webp" width="1600" height="900" srcset="https://css-irl.info/owning-your-web-01_1600.webp 1600w, https://css-irl.info/owning-your-web-01_1200.webp 1200w, https://css-irl.info/owning-your-web-01_900.webp 900w" sizes="(max-width: 1080px) 90vw, 930px" alt="Various top level domain names in pink, white and yellow on a dark purple background background" />
</figure>
<p>In case you missed it, <a href="https://matthiasott.com/">Matthias Ott</a> has a new newsletter. <a href="https://buttondown.email/ownyourweb">Own Your Web</a> is a lovely, fortnightly newsletter about “about designing, building, creating, and publishing for and on the Web”, which captures a whole bunch of people in its remit: designers, developers, writers, content creators and more. I was pleasantly surprised to get a shoutout in the <a href="https://buttondown.email/ownyourweb/archive/issue-02/">latest issue</a> for my recent article for MDN on web sustainability!</p>
<p>This issue asks <strong>“what is your website’s URL and why did you pick it?”</strong>. Although I neglected to answer Matthias’s original question on Mastodon, it got me thinking about this site’s URL and TLD (top-level domain). When I started this blog over four years ago, I picked the name “CSS IRL” because I thought it sounded catchy, and I felt it said something about what I was building: websites that work for real people (mainly SMEs and non-profits), helping fix problems, often finding the best compromise rather than the “ideal” solution. I can’t remember why I hyphenated the domain name. Perhaps “cssirl” was already taken, or I didn’t like the way it looked. I’m pretty okay with the name now, although part of me wonders whether I pigeonholed myself a little bit — since I often write about front end topics other than CSS.</p>
<p>Still, it hasn’t done me any harm. I also can’t help but think the hyphen helped me pick up some stray traffic from <a href="https://css-tricks.com/">css-tricks.com</a> 😅. Not something I considered when I chose it!</p>
<p>As Matthias notes, a lot of people pick their own name as their website URL. I have this for my <a href="https://michellebarker.co.uk/">personal website</a>, which is really just an “about me” page. I quite like having a separate blog, as it means that if one day I go off and make a name for myself in a wildly different field (like...as a drummer in a rock band?) I won’t have a blog with a load of web development content tied to my domain name.</p>
<p>“CSS IRL” also sounds more like a business than a personal name, which can be a good and bad thing. On the one hand, sounding like a business probably does contribute to the steady and increasing flow of traffic I get, and people seem to find it fairly memorable. On the other hand, I get a lot of email spam, mainly from marketing companies wanting to pay me to let them publish (probably AI generated) content on my site!</p>
<p>As for the TLD, “.info” is not a great one. I’m pretty sure I picked it because it was the cheapest option available at the time, and I didn’t expect this blog to have the longevity it has! But as Matthias says:</p>
<blockquote>
<p>People will visit your site for what you publish on it, not because you have the most sophisticated URL on the internet.</p>
</blockquote>
<p>This is absolutely true. My TLD has never done me any harm!</p>
<p>Don’t forget to <a href="https://buttondown.email/ownyourweb">subscribe to the Own Your Web newletter</a>.</p>
Code Gardening2023-11-04T00:00:00Zhttps://css-irl.info/code-gardening/<figure>
<img src="https://css-irl.info/code-gardening-01_900.webp" width="1600" height="900" srcset="https://css-irl.info/code-gardening-01_1600.webp 1600w, https://css-irl.info/code-gardening-01_1200.webp 1200w, https://css-irl.info/code-gardening-01_900.webp 900w" sizes="(max-width: 1080px) 90vw, 930px" alt="Vector illustration of a green plant sprouting against a deep purple background" />
</figure>
<p>In my spare time I do a bit of voluntary development work for an activist network. What I like about this kind of work (aside from helping a good cause) is that I get to experience an entirely different codebase, architecture and working process than I do in my paid employment.</p>
<p>I have a fair bit of front end development experience working with web agencies on a variety of projects, which gives me a good insight into different teams’ and individuals’ development processes, how to work within those, and when to introduce new processes or tools to help unify disparate ways of working. I’ve become pretty adept at refactoring, as well as knowing when to leave less-than-perfect code well alone. I built a lot of sites with the knowledge that they need to be maintained by people other than myself, with different skillsets.</p>
<p>In my current role at Ada Mode I mainly (but not exclusively) work on a single codebase for a software product in a small team, where I am a primary front end architect, decision maker, developer and maintainer. This requires some different skills and considerations. I’m partly coding for my future self, and with the knowledge that the project is a living codebase, one that will continually be maintained and reworked as the software is adapted, by people with a similar skillset to mine. I can spend time refactoring code that no longer serves its purpose, with less concern for client budgets, because I know that it’s time well spent to make the software more stable and maintainable.</p>
<h2>Planting seeds, pulling up weeds</h2>
<p>Working on a volunteer project is different again. In some respects it’s similar to agency work, in that I work with a team of varying skillsets. But the team is dispersed, and everyone’s time is stretched. If you raise a ticket, it might be a year before anyone picks it up. Complete a task, and it could be months before anyone gets around to merging it into production. Lots of tasks get started and then put on hold, as people’s other commitments pull their time away. In the meantime, other bugs creep in, and tickets pile up. Much like a gardener, you can keep tending the plants and pulling up weeds, but the work is never “done”.</p>
<p>It’s a sprawling project, in a constant state of flux. It’s been built and added to over the years by a lot of people all doing the best they can, not always specialising in the area they’re working on. That can make for some chaotic and unwieldy code.</p>
<p>Some might see this as frustrating (and it can be), but it’s not all bad. Doing this kind of work is a great way to level up and learn new skills. I recently spent some time getting to grips with static site generator <a href="https://gohugo.io/">Hugo</a>. Have an idea for a feature of improvement? Try it out. You don’t need to wait for permission. And every pull request is an opportunity for improvement.</p>
<h2>A bit less broken</h2>
<p>When I’m working on this kind of project it’s had to resist the urge to refactor everything. But that would easily spiral out of control, and no work would ever get deployed. A bit like a firefighter stopping to sweep up the debris while the fire rages out of control. Instead, I’ll settle for leaving things a bit less broken.</p>
<p>In my work on the front end, I’m forever spotting accessibility issues, no doubt baked in years ago. While working on a feature or bug fix, I’ll spend a few extra minutes restructuring the <a href="https://www.w3.org/WAI/tutorials/page-structure/headings/">heading levels</a> for a page, refactoring a particular piece of UI into a reusable component, or adding focus styles to a button. It’s not the kind of work that’s going to win awards, but perhaps it’ll mean that someone will be able to access the information who couldn’t before, or that next time another developer picks up the project it’ll be just a little bit easier to work with. I’ve no idea who came up with the term “code gardening”, but that’s exactly what it is.</p>
<h2>Embrace the chaos</h2>
<p>This is by no means the only type of project that can benefit from code gardening. But it’s perhaps one where its impact is most obvious. And like a gardener, the only real option is to embrace the chaos, throw aside perfectionist tendencies, and work with the nature of the project, not against it. And be kind to the other gardeners working alongside you, because they all care.</p>
Messing About with CSS Gradients2023-11-03T00:00:00Zhttps://css-irl.info/messing-about-with-css-gradients/<figure>
<img src="https://css-irl.info/messing-about-with-css-gradients-01_900.webp" srcset="https://css-irl.info/messing-about-with-css-gradients-01_1600.webp 1600w, https://css-irl.info/messing-about-with-css-gradients-01_1200.webp 1200w, https://css-irl.info/messing-about-with-css-gradients-01_900.webp 900w" sizes="(max-width: 1080px) 90vw, 930px" alt="A lattice pattern in orange, red and teal" />
</figure>
<p>I’m not a person who creates CSS “art” (as in drawings), but I do like messing about with CSS gradients and seeing what comes up. I think the first time I became aware that you could create some pretty cool effects with CSS gradients was Lea Verou’s <a href="https://projects.verou.me/css3patterns/">CSS3 Patterns Gallery</a>.</p>
<p>Gradients are applied with the <code>background-image</code> property, or can be combined with other properties in the <code>background</code> shorthand:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">div</span> <span class="token punctuation">{</span>
<span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token function">linear-gradient</span><span class="token punctuation">(</span>to right<span class="token punctuation">,</span> orange<span class="token punctuation">,</span> red<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Layering multiple gradients that include transparency can produce some pretty cool (and surprising effects), as I described in <a href="https://css-irl.info/building-the-zig-zag-gradient-lab/">an earlier post</a> (and accompanying talk). Playing around with gradients in conjunction with CSS background properties (<code>background-position</code>, <code>background-size</code>, <code>background-repeat</code>) is a great way to get to grips with those properties too.</p>
<p>Sometimes I enjoy spending 20 minutes layering up a few gradients in Codpen, just to scratch a creative itch. Today I made this demo, as I has the idea to create a sort of slightly 3D woven effect:</p>
<p class="codepen" data-height="426" data-default-tab="result" data-slug-hash="bGzwBWE" data-user="michellebarker" style="height: 426px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/bGzwBWE">
Lattice gradient</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>What I love while messing about with various colours and properties is that I often end up with happy accidents that actually add to the image.</p>
<h2>Custom properties</h2>
<p>Custom properties are super useful when working with CSS gradients, as there is a lot of repetition involved. Sometimes tweaking a custom property value can produce wildly different effects. Adjusting the angles of the gradients in the above example gives us something completely different (but still quite cool!).</p>
<figure>
<img src="https://css-irl.info/messing-about-with-css-gradients-02_900.webp" srcset="https://css-irl.info/messing-about-with-css-gradients-02_1600.webp 1600w, https://css-irl.info/messing-about-with-css-gradients-02_1200.webp 1200w, https://css-irl.info/messing-about-with-css-gradients-02_900.webp 900w" sizes="(max-width: 1080px) 90vw, 930px" alt="A pattern of overlapping triangles in orange, red and teal" />
</figure>
<p>Custom properties are also animatable in certain browsers (not Firefox yet!) with <code>@property</code>, like in this demo.</p>
<p class="codepen" data-height="424" data-default-tab="result" data-slug-hash="XWpVOmb" data-user="michellebarker" style="height: 424px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/XWpVOmb">
Rockin rainbows (Chromium only)</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<h2>Gradient pixel art</h2>
<p>A lot of CSS art is actually made with gradients. You can make pixel art by layering up a bunch of gradients and adjusting the <code>background-position</code> values. Here’s a pixel art maker I built a while back. Draw on the canvas and you can see the generated CSS.</p>
<figure>
<img src="https://css-irl.info/messing-about-with-css-gradients-03_900.webp" srcset="https://css-irl.info/messing-about-with-css-gradients-03_1600.webp 1600w, https://css-irl.info/messing-about-with-css-gradients-03_1200.webp 1200w, https://css-irl.info/messing-about-with-css-gradients-03_900.webp 900w" sizes="(max-width: 1080px) 90vw, 930px" alt="Screenshot of the pixel art generator" />
</figure>
<p><a href="https://codepen.io/michellebarker/pen/WNKbQOO">Try the demo</a></p>
Update on the COP28 Website2023-11-02T00:00:00Zhttps://css-irl.info/update-on-the-cop28-website/<p>Recently I wrote about the <a href="https://css-irl.info/greenwashing-and-the-cop28-website/">COP28 climate summit’s website</a>, which featured a low-carbon toggle that did precisely nothing. The post captured the interest of a few people, and I was contacted by ABC News Australia, who <a href="https://www.abc.net.au/news/2023-10-31/un-cop28-climate-summit-accused-greenwashing-website-low-carbon/103020978">published a story on it</a>, inviting me and web sustainability consultant Fershad Irani to comment.</p>
<p>Since I published the original post it appears the site has undergone some updates, and the low-carbon toggle actually prevents images from being downloaded. I have no idea whether the changes were made in response to the article or whether it’s something that would have been done anyway, but It’s a great first step. There’s still much more that can be done to build a site that’s truly “low carbon”, as Fershad details in his <a href="https://fershad.com/writing/cop28-uae-a-low-carbon-website-review/">review of the website</a>.</p>
<p>He also notes that while “low carbon” would ideally be the default, the low carbon version of the COP28 website leaves much to be desired. The design clearly hasn’t been considered at all: simply replacing the images with gradient blocks is unhelpful to the user.</p>
<blockquote>
<p>The low carbon experience of the COP28 website as it is today is not suitable to be the default version of the website. It would give a lot of users a very wrong impression of what low carbon web design is all about.</p>
</blockquote>
<p>I absolutely agree. With a little bit of design consideration, a low carbon site with no images (or fewer/smaller images) could be a better experience for the user, as well as helping them access the information they need faster.</p>
<h2>Encouraging signs for web sustainability</h2>
<p>It’s encouraging to see that web sustainability discourse is having an impact, and that positive change is possible. It goes to show how important it is to have these conversations, and highlight examples of good and bad practice.</p>
<p>There are plenty of resources available to help developers get started building low carbon sites. You can find some of them in my article for MDN, <a href="https://developer.mozilla.org/en-US/blog/introduction-to-web-sustainability/">An Introduction to Web Sustainability</a>.</p>
National Blog Posting Month2023-11-01T00:00:00Zhttps://css-irl.info/national-blog-posting-month/<p>Today I learned from <a href="https://amyhupe.co.uk/articles/nablopomo-2023-day-1/">Amy Hupe</a> that November is National Blog Posting Month, or #NaBloPoMo, if you will. I’ve always liked the idea of posting one thing a day (blog post, tip, sketch, photo, whatever), even while secretly thinking that people who commit to that must have a few screws loose. There’s a sense of achievement that comes from forcing myself to generate some sort of creative output regularly though.</p>
<p>I absolutely don’t have time to write a blog post every day for a month, but what the hey, let’s try this anyway and see how far we get! I’m anticipating I’ll crack within a week, but you know what? THAT’S OK! Trying is what matters here. Probably most of what I share will be tiny tips, links and commentary on other peoples’ projects or articles I’ve come across, or maybe the odd demo. In-depth posts and tutorials aren’t something I can churn out quickly. But I’m seeing this as a chance to do what I’ve intended to do for a while, which is share less on social networking sites and more on my own blog. Own your own content, and all that.</p>
<p>To kick off with some inspiration, here are a few people who have posted daily (or regular) content that I think is cool.</p>
<h2>Divtober by Lynn Fisher</h2>
<p><a href="https://lynnandtonic.com/">Lynn Fisher</a>, an awesome designer and CSS artist, regularly creates spectacular CSS illustrations with <a href="https://a.singlediv.com/">a single div</a>. During the month of October she publishes one a day for #divtober, and provides <a href="https://a.singlediv.com/divtober2023/">drawing prompts</a> so you can join her in the challenge.</p>
<figure>
<img src="https://css-irl.info/nbpm-01_900.webp" srcset="https://css-irl.info/nbpm-01_1600.webp 1600w, https://css-irl.info/nbpm-01_1200.webp 1200w, https://css-irl.info/nbpm-01_900.webp 900w" sizes="(max-width: 1080px) 90vw, 930px" alt="Illustrations of an x-ray, a round flask and crystals, a crystal ball, and a glass jug filled with purple liquid with a spoon" />
<figcaption>A few single div illustrations by Lynn Fisher</figcaption>
</figure>
<h2>100 Days of More or Less Modern CSS by Manuel Matuzović</h2>
<p>Last year Manuel took on the challenge of getting himself up to speed with modern CSS. He did that by writing a blog post on a different CSS feature every day for <a href="https://www.matuzo.at/blog/2022/100-days-of-more-or-less-modern-css/">100 days</a>(!). This has ended up as an awesome resource for learning about new (or new-ish) CSS features through concise and well-written articles.</p>
<h2>Stefan Judis’s TIL</h2>
<p>I often find myself on prolific writer <a href="https://www.stefanjudis.com/">Stefan Judis</a>’s blog, which is packed with useful web development tips. I love that he has a whole <a href="https://www.stefanjudis.com/today-i-learned/">Today I learned</a> section, full of quick blog posts where he shares his learnings. These kind of blog posts are a gift to your future self, and something I’d like to get back to doing a bit more. Great inspiration for a month of blogging!</p>
Finding Meaning in Our Work2023-10-24T00:00:00Zhttps://css-irl.info/finding-meaning-in-our-work/<figure>
<img src="https://css-irl.info/finding-meaning-in-our-work-01__900.webp" alt="Comic excerpt in which Naomi (yellow tadpole) and Dad (a fish) are talking at bedtime.
Dad: “Why are you in such a hurry to be rich?”
Naomi: “I wanna buy stuff! I really like stuff.”
Dad: “Okay. But what then?”
(Naomi frowns)
Dad: “What happens after you buy everything?”
(Naomi looks sad)
Dad: “What’s your purpose? How will your money make your life better?”
Naomi: “Ummm...I won’t have to work as hard!”
Dad: “Okay. But you’ll have to work hard to be rich, right?”
Naomi: “Yeah, probably.”" width="1600" height="900" srcset="https://css-irl.info/finding-meaning-in-our-work-01__1600.webp 1650w, https://css-irl.info/finding-meaning-in-our-work-01__1200.webp 1200w, https://css-irl.info/finding-meaning-in-our-work-01__900.webp 900w, https://css-irl.info/finding-meaning-in-our-work-01__600.webp 600w" sizes="(min-width: 1086px) 75vw, (min-width: 1264px) 930px, 90vw" />
<figcaption>Spread from the wonderful ‘Cat Kid Comic Club: On Purpose’, written and illustrated by Dav Pilkey</figcaption>
</figure>
<p>This past year I’ve been thinking a lot about meaningful work, trying to square my own work as a web developer with my personal values and the impact I want to have on the world.</p>
<p>It feels especially necessary during a time when multiple crises are engulfing the world — the climate crisis and its knock-on effects, war, extreme poverty, and the displacement of people affected by all of these — to reflect on how we can contribute a net good to society. For me, that manifests itself in examining my choices (of technology, of career opportunities, of the freelance assignments I take on) through that lens, as well as choosing to prioritise the people and communities precious to me (online and offline), instead of chasing social media engagement. Sometimes it means standing up for my beliefs, or advocating for others who need to be heard.</p>
<p>Like many others, I’ve fallen a little out of love with technology and its shiny promises of quick fixes to our problems, now that we’re increasingly seeing what it’s capable of in the hands of a few entitled billionaires. But I’m discovering healthy pockets of tech criticism, particularly in the web standards and web sustainability communities, which gives me hope. And spending more time offline gives me a better perspective on how the products and services we build fit into the real world — how they can help people, and where they fall short.</p>
<p>I haven’t got it all figured out, and will doubtless fail to hit my lofty goals from time to time. But one thing’s for certain: we can’t do this alone. Meaning comes from connections, and connections make things happen. And as developers, connecting things is what we do.</p>
<h2>It all means nothing in the end</h2>
<p><a href="https://amyhupe.co.uk/">Amy Hupe</a>’s talk at State of the Browser this year, titled ‘It all means nothing in the end’ really struck a chord, and is one of the most insightful talks I’ve seen recently. It came at the perfect time, and there were so many moments when I found myself nodding along. I highly recommend watching it.</p>
<p>https://youtu.be/Q0v2YJLq8n8</p>
<h2>The Luddites had a point</h2>
<p>Recently there has been a reframing of the term “luddite” as people begin to question the narrative around technological progress and who it really serves. There’s a great episode of <a href="https://www.techwontsave.us/">Tech Won’t Save Us</a>, a podcast that looks critically at the tech industry, where Paris Marx interviews Brian Merchant about his new book on the history of the Luddites. It’s a great reminder of how important it is to challenge the stories sold to us by powerful tech companies.</p>
<p><a href="https://www.techwontsave.us/episode/187_the_real_history_of_the_luddites_w_brian_merchant">Listen to the episode</a></p>
<h2>Current reading: Doppelganger</h2>
<p>I’m reading Naomi Klein’s new book, ‘Doppelganger’, which explorers how conspiracy theories, misinformation and far-right views gain traction online. She also notes how the personal brands or digital selves we build online are polished versions of ourselves, where we feel we can exert some sort of control, while what we really need is to build ideas, to organise collectively. She asks:</p>
<blockquote>
<p>What <strong>aren’t</strong> we building when we are building our brands?</p>
</blockquote>
Introduction to Web Sustainability2023-10-13T00:00:00Zhttps://css-irl.info/introduction-to-web-sustainability/<p>It’s important to reflect on the environmental impact of our work and do what we can to reduce it. Don’t know where to start? This article for MDN introduces some ways in which developers can improve the efficiency of the website we build and take steps to make our work more sustainable.</p>
<p><a href="https://developer.mozilla.org/en-US/blog/introduction-to-web-sustainability/">Read the article</a></p>
Styling External Links with Attribute Selectors2023-10-11T00:00:00Zhttps://css-irl.info/styling-external-links-with-attribute-selectors/<p>You might notice on some websites you visit that external links display a little icon next to them. This is super helpful for users, as it lets them know that the link is going to take them somewhere off-site.</p>
<p>I’ve just recently implemented custom styling for external links within posts on this site. The good news is we don’t need to append a special class to external links or alter the markup in any way. We can simply use an attribute selector.</p>
<h2>Using attribute selectors</h2>
<p>CSS allows us to style HTML elements based on their attributes by wrapping them in square brackets. For example, we can style any element with the <code>hidden</code> attribute to have a <code>display</code> property of <code>none</code>:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">[hidden]</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>We can also style elements when the attribute equals a particular value. For form elements, we could style particular input types without having to use a class:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">input[type='checkbox']</span> <span class="token punctuation">{</span>
<span class="token property">accent-color</span><span class="token punctuation">:</span> deeppink<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>For our external links we want to apply the styling when the <code>href</code> attribute contains a link to an external site. We don’t know exactly what that value will be (and it wouldn’t be practical to add each individual URL to our stylesheet!), but we know that internal links (links to other posts on the site) will begin with a slash, whereas external links will begin with <code>https://</code>. So we can style only the links that begin with <code>http</code> by inserting a <code>^</code> character into our attribute selector:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">a[href^='http']</span> <span class="token punctuation">{</span>
<span class="token comment">/* Styles for external links */</span>
<span class="token punctuation">}</span></code></pre>
<p>Alternatively we can use other operators to determine different styling conditions:</p>
<pre class="language-css"><code class="language-css"><span class="token comment">/* Matches the URL exacly */</span>
<span class="token selector">a[href='https://css-irl.info']</span>
<span class="token punctuation">{</span>
<span class="token punctuation">}</span>
<span class="token comment">/* Has 'css' anywhere in the URL */</span>
<span class="token selector">a[href*='css']</span> <span class="token punctuation">{</span>
<span class="token punctuation">}</span>
<span class="token comment">/* Ends with .info */</span>
<span class="token selector">a[href$='.info']</span> <span class="token punctuation">{</span>
<span class="token punctuation">}</span>
<span class="token comment">/* Class contains the word 'link' */</span>
<span class="token selector">a[class~='link']</span> <span class="token punctuation">{</span>
<span class="token punctuation">}</span></code></pre>
<p>Additionally, by adding <code>s</code> or <code>i</code> before the end bracket we can control whether they are compared case-sensitively or insensitively:</p>
<pre class="language-css"><code class="language-css"><span class="token comment">/* Case sensitive */</span>
<span class="token selector">a[href*='css-irl' s]</span> <span class="token punctuation">{</span>
<span class="token punctuation">}</span>
<span class="token comment">/* Case insensitive */</span>
<span class="token selector">a[href*='css-irl' i]</span> <span class="token punctuation">{</span>
<span class="token punctuation">}</span></code></pre>
<h2>Styling the pseudo element</h2>
<p>For our external links, we’ll append an icon by styling a pseudo element. Here we’re using the <code>content</code> property with a base64 encoded SVG, as the icon is very simple. But you could use an image URL, another character, or an emoji. We can add a small margin to position it slightly away from the text.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">a[href^='http']::after</span> <span class="token punctuation">{</span>
<span class="token property">content</span><span class="token punctuation">:</span> <span class="token url"><span class="token function">url</span><span class="token punctuation">(</span><span class="token string url">"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewbox='0 0 12.2 12.2' width='14' height='14'%3E%3Cpath d='M5.7 0v1.5h4L4.5 6.7l1 1.1 5.3-5.2v3.9h1.4V0z'/%3E%3Cpath fill='none' d='M3.4 6.7l3-2.9H1.5v7h7V5.9l-3 2.9z'/%3E%3Cpath d='M8.5 5.9v4.9h-7v-7h4.9l1.5-1.6H0v10h10V4.4z'/%3E%3C/svg%3E"</span><span class="token punctuation">)</span></span><span class="token punctuation">;</span>
<span class="token property">margin-left</span><span class="token punctuation">:</span> 0.25em<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>One problem with using <code>content</code> with an SVG is we don’t have full control over the size of the icon. It uses the intrinsic dimensions of the SVG. If we want to apply the icon to <strong>any</strong> external link regardless of font size (e.g. a heading), we might be better off using the <code>background-image</code> property. We can set a width and height (in ems, which are relative to font size), and use <code>background-size</code> to ensure our SVG covers the entire area.</p>
<p>We need to set the <code>content</code> property to an empty string, otherwise the pseudo element won’t render. We also need to set the <code>display</code> property to <code>inline-block</code>.</p>
<p>(Note: I’m using a custom property for the image URL, for brevity.)</p>
<pre class="language-css"><code class="language-css"><span class="token selector">a[href^='http']::after</span> <span class="token punctuation">{</span>
<span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">''</span><span class="token punctuation">;</span>
<span class="token property">display</span><span class="token punctuation">:</span> inline-block<span class="token punctuation">;</span>
<span class="token property">width</span><span class="token punctuation">:</span> 1em<span class="token punctuation">;</span>
<span class="token property">height</span><span class="token punctuation">:</span> 1em<span class="token punctuation">;</span>
<span class="token property">margin-left</span><span class="token punctuation">:</span> 0.25em<span class="token punctuation">;</span>
<span class="token property">background-size</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>
<span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token function">url</span><span class="token punctuation">(</span><span class="token function">--var</span><span class="token punctuation">(</span>svgUrl<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h2>Preventing an ”orphan” icon</h2>
<p>There’s one more thing we can improve about this approach. Currently it’s possible for the icon to wrap onto the next line of text, leaving an undesirable orphan icon all on its own.</p>
<figure style="max-width: 40rem">
<img src="https://css-irl.info/styling-external-links-01.webp" width="900" height="443" alt="Two external links with appended icon. In the second link the icon wraps onto the next line" />
</figure>
<p>If we add <code>position: absolute</code> to the pseudo element, and a bit of right padding to the anchor element, then the icon will never wrap by itself.</p>
<figure style="max-width: 40rem">
<img src="https://css-irl.info/styling-external-links-02.webp" width="900" height="443" alt="Two external links with appended icon. The icon no longer wraps on its own" />
</figure>
<pre class="language-css"><code class="language-css"><span class="token selector">a[href^='http']</span> <span class="token punctuation">{</span>
<span class="token property">padding-right</span><span class="token punctuation">:</span> 1.25em<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">a[href^='http']::after</span> <span class="token punctuation">{</span>
<span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span>
<span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">''</span><span class="token punctuation">;</span>
<span class="token property">display</span><span class="token punctuation">:</span> inline-block<span class="token punctuation">;</span>
<span class="token property">width</span><span class="token punctuation">:</span> 1em<span class="token punctuation">;</span>
<span class="token property">height</span><span class="token punctuation">:</span> 1em<span class="token punctuation">;</span>
<span class="token property">margin-left</span><span class="token punctuation">:</span> 0.25em<span class="token punctuation">;</span>
<span class="token property">background-size</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>
<span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token function">url</span><span class="token punctuation">(</span><span class="token function">--var</span><span class="token punctuation">(</span>svgUrl<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<aside>
<p>Unfortunately this trick doesn’t work in the latest version of Chrome.</p>
</aside>
<p>Here’s the full code:</p>
<p class="codepen" data-height="300" data-default-tab="css,result" data-slug-hash="XWoGgrX" data-user="michellebarker" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/XWoGgrX">
Untitled</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
Greenwashing and the COP28 Website2023-10-10T00:00:00Zhttps://css-irl.info/greenwashing-and-the-cop28-website/<p><strong>Update (28th October 2023): Since this post was published the COP28 website has undergone some changes. Many of the images are now better optimised, and the low-carbon toggle actually prevents them from loading. Much more could be done to optimise the site, however, and many of the points still stand.</strong></p>
<p>A member of the <a href="https://www.w3.org/community/sustyweb/">Sustainable Web Design Community Group</a> recently highlighted some sustainability issues with the <a href="https://www.cop28.com/">COP28 website</a> that warrant a closer look.</p>
<figure>
<img src="https://css-irl.info/greenwashing-and-the-cop28-website_900.webp" srcset="https://css-irl.info/greenwashing-and-the-cop28-website_1600.webp 1600w, https://css-irl.info/greenwashing-and-the-cop28-website_1200.webp 1200w, https://css-irl.info/greenwashing-and-the-cop28-website_900.webp 900w, https://css-irl.info/greenwashing-and-the-cop28-website_600.webp 600w" sizes="(max-width: 1080px) 90vw, 930px" alt="Screenshot of the COP28 website" />
<figcaption>The COP28 website</figcaption>
</figure>
<p>The COP28 climate summit, due to take place in the United Arab Emirates this year is somewhat controversial for a number of reasons: the fossil fuel wealth upon which much of the country is built, the <a href="https://www.theguardian.com/environment/2023/oct/07/meet-the-oil-man-tasked-with-saving-the-planet-cop28">man chosen to lead the conference</a> (who happens to be head of the UAE’s national oil company), and the UAE’s <a href="https://www.amnesty.org/en/location/middle-east-and-north-africa/united-arab-emirates/report-united-arab-emirates/">questionable record on human rights</a>, including imprisoning peaceful protesters.</p>
<p>By hosting the summit, the UAE has been accused of attempting to greenwash its reputation. We can also see some greenwashing of a different kind taking place on the summit’s <a href="https://www.cop28.com/">website</a>. Let’s take a look at how green the site really is.</p>
<h2>Page weight</h2>
<p>Inspecting the network panel in dev tools, we can see the initial data transfer of the site on a desktop computer is 7.1MB for the homepage. Scrolling down to the bottom of the page, a total of 12.9MB is transferred. That’s far higher than the <a href="https://httparchive.org/reports/page-weight?start=2017_04_15&end=latest&view=list">2.5MB average</a> (which is still too high in most cases). The three worst offenders are images, JavaScript and fonts.</p>
<h3>Images</h3>
<p>Images account for the greatest proportion of data transfer — over 11MB of the total. Many of those individual images are approaching 2MB in size. Although some are lazyloaded, they could be far better optimised. In one case, an 8000px wide image is being displayed as a small thumbnail. The hero images are loaded as background images in CSS, which means they can’t be made properly responsive and will always load the large file, regardless of the final display size. Using <a href="https://developer.mozilla.org/en-US/docs/Learn/HTML/Multimedia_and_embedding/Responsive_images"><code>srcset</code></a> and properly sizing images could reduce the bulk of the data transfer for the site.</p>
<p>Several of the homepage images could benefit from being converted to <a href="https://developers.google.com/speed/webp#:~:text=WebP%20is%20a%20modern%20image,that%20make%20the%20web%20faster.">WebP</a>. WebP images are smaller in size that JPG and PNG images, without any loss of quality. (Fershad Irani tests the power consumption of the various image formats in his article <a href="https://fershad.com/writing/power-consumption-jpeg-webp-and-avif/">Power consumption of JPEG, WebP, and AVIF</a>.) But several of the larger images on the site are PNGs.</p>
<h3>JavaScript</h3>
<p>The total JS transfer size for the homepage is nearly 500kB, which seems high for a relatively simple page. The great thing is the homepage (and much of the site) works perfectly well with JS disabled! So why do we need it at all? Some JS is necessary for the mobile menu to function correctly. Other than that, it appears to be responsible for the hero carousel, the “live” countdown, and the pop-out video player. I’ll wager the JS bundle could be significantly reduced, and removing (or simplifying) those features doesn’t worsen the user experience.</p>
<p>Google Tag Manager scripts also add to the JS payload. Removing them is always a performance win, but good luck arguing that with your marketing department. They should probably be behind a cookie banner here though.</p>
<h3>Fonts</h3>
<p>The page loads four variants of the same font, all as TTF files, which are usually much larger than the well-supported WOFF2 format, and could easily be converted. There’s a much smaller font file in addition, which I would guess is maybe a font subset. Considering a variable font could save on bytes downloaded for the end user.</p>
<h2>A fake low-carbon toggle</h2>
<p>The site features a prominent toggle at the top of the page, inviting users to switch to the “low carbon” version. Great! Some awareness of digital sustainability, featured prominently on a popular, wide-reaching site! Not so fast...there are some significant problems here.</p>
<h3>1. The low carbon toggle does absolutely nothing</h3>
<p>In fact, worse than nothing. It doesn’t prevent images being downloaded. It doesn’t switch the site to dark mode, or prevent autoplaying animations (e.g. the hero carousel), or reduce resources transferred in other way. All it does is overlay an extra element with a background gradient on top of the large images on the site to give the <strong>appearance</strong> that those images being prevented from loading. The user’s preference is stored in local storage, meaning it would be possible to check the user’s preference on a return visit and only load certain JS modules if they’ve requested the full experience, for instance. But that’s not what’s happening here. It simply gives the user the impression that they (and the website owners) are considering the planet, without actually changing anything. A classic example of greenwashing.</p>
<h3>2. Low carbon should be the default</h3>
<p>It makes far more sense for an environmental perspective for low carbon to be the default. But the time the user sees they can choose the “low carbon” option, it’s already too late, the full version has downloaded. It would be even better to remove the high carbon alternative and ensure the site as a whole is low-carbon by design. A toggle feels like a sticking-plaster solution, even if it were <strong>actually</strong> effective.</p>
<h3>3. Problematic language</h3>
<p>If we want people to make low carbon choices, we should avoid giving the impression that we are taking something away, or that they’re somehow getting a lesser experience. The toggle label when low carbon mode is toggled on, “Switch back to full experience”, makes it sound like users are missing out on something by switching on low carbon mode, when in fact (if done right) they could be gaining a better, faster experience. This is something Asim Hussain touches upon in <a href="https://youtu.be/Djrgi2dqWgA?si=7ywUeIPffloQ_f-c">his talk at Smashing Conf Freiburg</a>.</p>
<h2>Summary</h2>
<p>There’s a lot more to unpick when it comes to the sustainability of any website, and we’ve only touch upon a few aspects here. <a href="https://fershad.com/">Fershad Irani</a> has analysed the last two COP summit websites on his blog, and will no doubt be able to go into a lot more detail on this one.</p>
<p>When a website is visited by hundreds of thousands (or even millions) of users, the carbon savings that could be made by optimising the site, or making it genuinely low-carbon from the outset could be significant. Failing to prioritise this on the global stage, while not the most problematic aspect of the summit, feels like a missed opportunity.</p>
Introducing the Web Sustainability Guidelines2023-10-06T00:00:00Zhttps://css-irl.info/introducing-the-web-sustainability-guidelines/<figure>
<img src="https://css-irl.info/introducing-the-web-sustainability-guidelines.png" alt="Text: Introducing the web sustainability guidelines. W3C Sustainable Web Design (SustyWeb) Comminity Group" width="960" height="540" />
</figure>
<p>The <a href="https://www.w3.org/community/sustyweb/">Sustainable Web Design Community Group</a> has just launched a draft <a href="https://w3c.github.io/sustyweb/">set of guidelines</a> for designing and developing sustainable web applications. Covering design, UX, front and back end development, and business strategies, these guidelines have been months in the making, with contributions from experts and breakout groups from across the industry.</p>
<p>The document could form the basis of a checklist for implementing sustainable web products, or individual practitioners could isolate the parts that apply to them. Rather than providing too much detail, it features links to resources where designers and developers can explore particular areas in depth and further their understanding. The hope is that individuals of all levels will be able to start building with sustainability in mind.</p>
<p>There’s also a scoring system showing the impact and effort of the various measures. Although the level of effort required is likely to vary depending on individuals’ expertise and the stage of the project, the guidelines could be useful in helping organisations deciding which areas to prioritise and focus resources.</p>
<h2>Contribute</h2>
<p>The community group welcomes feedback and contributions to the guidelines. <a href="https://www.w3.org/community/sustyweb/">Join the group</a>, or file an issue on <a href="https://github.com/w3c/sustyweb/">Github</a>. Hopefully by publishing a widely agreed upon set of guidelines we can increase the awareness adoption of sustainable web design practices.</p>
<p><a href="https://w3c.github.io/sustyweb/">Read the guidelines →</a></p>
Reactivity in Vanilla Javascript2023-09-22T00:00:00Zhttps://css-irl.info/reactivity-in-vanilla-javascript/<p>When we talk about the concept of reactivity in Javascript, we generally mean a variable in our code responding to an event or change in state somewhere else in our code. Perhaps we need a button to respond to a <code>'click'</code> event, or maybe we need to update the DOM in response to new data. <a href="https://vuejs.org/guide/extras/reactivity-in-depth.html#what-is-reactivity">The Vue docs have a great explanation of reactivity</a>.</p>
<p>This Frontend Masters article, <a href="https://frontendmasters.com/blog/vanilla-javascript-reactivity/">Patterns for Reactivity with Modern Javascript</a> by Marc Grabanski recently caught my attention. I consider myself a reasonably proficient JS developer (it took a long time to feel that way!) and work with it daily, but I wasn’t familar with several of the patterns in this article. That’s partly because day-to-day I work with <a href="https://vuejs.org/">Vue</a>, and for building a large web app using a framework seemed like the obvious choice. But <strong>should</strong> it be? How hard would it be to build an app with these techniques in vanilla JS, which after all, are web standards and don’t carry the dependency overhead of a framework?</p>
<p>To my mind it would be <strong>very</strong> hard. JS frameworks largely exist to take away some of the pain of this kind of thing (while giving us a whole different kind of pain, LOL), and actually use these techniques under the hood. But that doesn’t mean we shouldn’t question our choices every now and then, and maybe JS frameworks shouldn’t be the first tool we reach for for small pieces of reactivity. Either way, it’s super useful to learn about these patterns.</p>
<p><a href="https://frontendmasters.com/blog/vanilla-javascript-reactivity/">Read the article</a></p>
Cool Tools2023-09-10T00:00:00Zhttps://css-irl.info/cool-tools/<p>Chris Brandrick at <a href="https://frontendfoc.us/">Frontend Focus</a> asked me to share five tools, libraries or packages that I love for the <a href="https://frontendfoc.us/issues/608">latest issue</a> of the newsletter. It was hard to narrow it down to just five! So I thought I’d share the full long-list with you here, in no particular order.</p>
<h2><a href="https://parceljs.org/">Parcel</a></h2>
<p>I hate having to spend hours configuring a new project when I just want to jump straight into some code, which is why Parcel is my go-to bundler. It’s “zero config”, and makes it quick and easy to set up projects.</p>
<h2><a href="https://www.webpagetest.org/carbon-control/">WebPageTest: Carbon Control</a></h2>
<p>WebPageTest measures your site’s performance, and has recently added a new feature, Carbon Control, which helps to measure the CO2 impact, along with tips for reducing it.</p>
<h2><a href="https://utopia.fyi/">Utopia</a></h2>
<p>I somehow missed this one off the original list, despite using it for nearly every project. Utopia (by James Gilyead and Trys Mudford) provides configurable set of CSS custom properties for fluid sizing and typography, with options to use a modular scale. It’s a godsend, and helps to avoid writing media queries all over the place!</p>
<h2><a href="https://github.com/paulirish/lite-youtube-embed">Lite YouTube Embed</a></h2>
<p>Embedded videos can be responsible for performance problems (and CO2 emissions) of a site. This package by Paul Irish ensures they are only loaded upon interaction, providing a better (faster) user experience.</p>
<h2><a href="https://jakearchibald.github.io/svgomg/">SVG OMG</a></h2>
<p>Jake Archibald’s tool for minifying and compressing SVGs is indispensable for any developer working with SVG.</p>
<h2><a href="https://svg-path-visualizer.netlify.app/#M140%2020C73%2020%2020%2074%2020%20140c0%20135%20136%20170%20228%20303%2088-132%20229-173%20229-303%200-66-54-120-120-120-48%200-90%2028-109%2069-19-41-60-69-108-69z">SVG Path Visualizer</a></h2>
<p>A really cool playground to help you understand how SVG path syntax works.</p>
<h2><a href="https://yoksel.github.io/url-encoder/">URL Encoder for SVG</a></h2>
<p>This simple tool takes an SVG and converts it to a base64 encodes URL, for use in CSS properties such as <code>background-image</code> and <code>mask-image</code>.</p>
<h2><a href="https://firefox-source-docs.mozilla.org/devtools-user/">Firefox Dev Tools</a></h2>
<p>Firefox is my primary development browser and has a bunch of handy inspection tools for working with CSS, including a color contrast accessibility checker, inspectors for fonts, layout and animation, a changes panel, validity and compatibility checkers. It was too hard to pick just one favourite feature!</p>
<h2><a href="https://cubic-bezier.com/#.17,.67,.83,.67">Cubic Bezier</a></h2>
<p>Lea Verou’s visualizer for cubic bezier easing curves for CSS animations is one I use regularly for crafting more natural-looking animations.</p>
<h2><a href="https://marketplace.visualstudio.com/items?itemName=EcksDy.env-switcher">ENV Switcher for VS Code</a></h2>
<p>This VS Code extension enables easy switching between different .env files in a project.</p>
<h2><a href="https://aremythirdpartiesgreen.com/">Are my third parties green?</a></h2>
<p>A tool by Fershad Irani for checking if your third-party scripts use green hosting.</p>
<h2><a href="https://tinypng.com/">Tiny PNG</a></h2>
<p>Build tools can be configured to compress images, but we can reduce energy use even more by not uploading large images in the first place! Tiny PNG squooshes not just PNG files, but JPG and WebP too, with no visible loss of quality.</p>
<h2><a href="https://marketplace.visualstudio.com/items?itemName=wix.vscode-import-cost">Import Cost for VS Code</a></h2>
<p>An extension for VS Code that shows the size of your JS imports in a file. It helps you become aware of the worst offenders, and consider more lightweight alternatives.</p>
<h2><a href="https://d3js.org/">D3</a></h2>
<p>If you work with data visualisation on the web you’ll likely already know D3.js. But I wanted to include it here as it’s a great library, and one that I use almost every day of my working life at Ada Mode. In addition to data viz, it has useful modules for working with arrays, date and time formatting, SVG in general, and much more.</p>
<p>I’m sure I’m missing a bunch! What are your favourite tools? Let me know!</p>
Creating custom easing effects in CSS animations using the linear() function2023-08-01T00:00:00Zhttps://css-irl.info/creating-custom-easing-effects-with-linear/<p>An animation is about more than just moving things from one place to another. <strong>How</strong> something moves (or changes in some way) is just as important for conveying a sense of purpose.</p>
<p>The <code>linear()</code> function in CSS is a new easing function that gives us more control over crafting our animations. We'll explore how <code>linear()</code> works and also look at some practical examples of where it can be used.</p>
<p><a href="https://developer.mozilla.org/en-US/blog/custom-easing-in-css-with-linear/">Read the article</a></p>
Video: Building a Greener Web2023-07-25T00:00:00Zhttps://css-irl.info/video-building-a-greener-web/<p>It’s been great to see digital sustainability become more of a mainstream topic in recent months. With most of Europe baking in a deadly heatwave with unprecedated temperature, the need for action on climate change is more pressing than ever. If you’re not sure what action you, as a developer, can take, my talk from <a href="https://heypresents.com/conferences/2023">All Day Hey</a> conference provides some facts, figures and starting points for thinking about sustainability in the context of web development.</p>
<p>Although it may seem like a big, scary topic, there’s a lot we can do in terms of mitigating environmental harm in our own work, sharing knowledge and influencing the industry as a whole. I hope you’ll find some useful takeaways here, and be encouraged to join the fight against climate change.</p>
<p>https://www.youtube.com/watch?v=EfPoOt7T5lg</p>
Scroll Progress Animations in CSS2023-07-14T00:00:00Zhttps://css-irl.info/scroll-progress-animations-in-css/<p>Scroll-linked animations can often add a touch of class to a website, but have long been the preserve of JavaScript. Now a brand new specification is being implemented to enable us to create rich scroll-driven experiences with CSS! In this article for MDN, we’ll explore scroll timelines — how to link an animation to the progress of scroll.</p>
<p><a href="https://developer.mozilla.org/en-US/blog/scroll-progress-animations-in-css/">Read the article</a></p>
Video: Modern CSS Layout is Awesome2023-07-11T00:00:00Zhttps://css-irl.info/video-modern-css-layout-is-awesome/<p>Back in May I had the privilege of speaking at <a href="https://beyondtellerrand.com/">Beyond Tellerand</a>, an incredible conference in Düsseldorf, Germany, with content extending way beyond web design into the realms of animation, mural painting, typography, digital art, Japanese calligraphy and more!</p>
<p>My own talk was on CSS layout, including some of the exciting new features we have at our disposal. If you want to get up to speed on what’s new in CSS layout, this is a good place to start.</p>
<p>https://www.youtube.com/watch?v=6O0KBNslevQ</p>
<p>Here’s a <a href="https://beyondtellerrand.com/blog/wrap-up-and-coverage-for-dusseldorf-2023">wrap up from the conference</a> too. Enjoy!</p>
Thoughts From CSS Day2023-06-13T00:00:00Zhttps://css-irl.info/thoughts-from-css-day-2023/<p>Last week I had the privilege of attending <a href="https://cssday.nl/2023">CSS Day</a> for the second year in a row (and MCing the second day!). I have to say it’s pretty much the most inspiring conference I’ve ever been to. Right off the bat, <a href="https://una.im/">Una</a> hit the nail on the head when she said that (paraphrasing) if we thought 2022 was a good year for CSS, we ain’t seen nothin’ yet.</p>
<p>Una’s talk was a whistle-stop tour of a huge number of features recently or just about to be released, which was mind-blowing in itself, but what was even more striking was the number of features <strong>she didn’t even have time to cover</strong> — a whole slide’s worth. It illustrates the blazing pace at which CSS is evolving right now, which as a CSS person, I’m very happy to see.</p>
<p>But I can understand why some people who aren’t quite as deeply immersed in the world of CSS might be concerned about keeping up. It’s clear that companies don’t value CSS skills in the same way as, say Javascript — which is reflected in pay disparity, bootcamp priorities, and the lack of visibility in job descriptions. It’s not uncommon to see front end job specifications listing React, Redux, Typescript and more, with barely a passing mention of HTML and CSS, despite being core web technologies. New developers are encouraged to learn just enough CSS to get by, rather than cultivate a deep knowledge and appreciation for the language, and that’s reflected in the messy, convoluted code, riddled with bad practices, that many of us have to clear up afterwards.</p>
<p>I’m not going to speculate on all the reasons why this is, as plenty of others have their own theories. But for me, being at CSS Day felt simultaneously energising and heartbreaking: while it was fantastic to spend two days in the company of so many people who care about our chosen craft, it was also a reminder that it’s one of the only places it’s truly valued.</p>
<p>On the other hand, one or two people mentioned that the CSS community seems actively hostile towards JS, which is a real shame. I don’t think it’s true for the most part (many of us <strong>are</strong> JS developers, and have a far more nuanced opinion), but we can certainly come across as having a chip on our shoulder. It certainly doesn’t help those from outside of our bubble feel welcome, and I think we need to be conscious of that in the language we use. Taking an adversarial approach to web development doesn't do us any favours when we want to build a bigger tent, not shut others out.</p>
<p>Una ended her talk with a call-to-arms to go out into the world and share our CSS knowledge, to build the CSS community. I can’t help thinking that’s essential if we want to establish proper appreciation of CSS skills.</p>
<h2>CSS for all</h2>
<p>Another thing that was notable was Google’s dominance in the world of CSS. As well as two speakers (and one MC) from the Chrome Dev Rel team, Google also sponsored the help desk, and hosted a panel during one of the breaks. While I’m glad that Google is investing in CSS, I’m slightly wary of the lack of representation of other browsers. I don’t know how we address that as a community, other than other browsers stepping up and putting forward their own representatives. As <a href="https://paulrobertlloyd.com/2023/162/a1/css_day/">Paul Robert Lloyd</a> puts it:</p>
<blockquote>
<p>A web dictated by Google would be a real tragedy.</p>
</blockquote>
<p>The web needs diverse voices to thrive, and it shouldn’t come down to one company to determine the future of its fundamental building blocks.</p>
<h2>Levelling up</h2>
<p><a href="https://www.matuzo.at/">Manuel</a> closed out the conference with a rundown of some awesome new features he discovered in his explorations of modern CSS and how we can use these in practical ways. It was great to see how he had levelled up his CSS knowledge, and is a timely reminder that all that’s really needed is a bit of time, care and focus. People like Manuel documenting their experience are part of what makes the community great, and part of how we can broaden it to include more people.</p>
<p>I can’t wait for next year’s CSS Day, but I also hope that in the meantime there will be more specialist CSS events to get involved with.</p>
<p>Some more write-ups:</p>
<ul>
<li><a href="https://adactio.com/journal/20238">Days of style and standards</a> by Jeremy Keith</li>
<li><a href="https://www.matuzo.at/blog/2023/css-css-css/">CSS! CSS! CSS!</a> by Manual Matuzović</li>
<li><a href="https://utilitybend.com/blog/this-was-css-day-2023">This was CSS Day 2023</a> by Brecht</li>
<li><a href="https://paulrobertlloyd.com/2023/162/a1/css_day/">The continuing tragedy of CSS: thoughts from CSS Day 2023</a> by Paul Robert Lloyd</li>
<li><a href="https://johanronsse.be/2023/06/11/css-day-2023/">CSS Day 2023</a> by Johan Rosse</li>
</ul>
Reducing Complexity in Front End Development2023-06-06T00:00:00Zhttps://css-irl.info/reducing-complexity-in-front-end-development/<p>One of my favourite sessions at <a href="https://heypresents.com/conferences/2023">All Day Hey</a> conference last month was Jack Franklin’s talk <a href="https://heypresents.com/talks/abstractions-complexities-and-off-ramps">Abstractions, complexities and off-ramps</a>. As web applications grow larger, they inevitably fall prey to complexity, despite our best intentions. The prevalence of third-party libraries to solve everyday coding problems is both a blessing and a curse. It can be tempting to reach for an off-the-shelf solution to save time now, but what happens when that dependency is no longer maintained, or when developer needs change? Can you be sure you understand enough of the underlying code to fix any bugs created by pinning all-important functionality on an (often unpaid) open source maintainer?</p>
<p>We tend to <code>npm install</code> third-party dependencies unthinkingly. But these packages often rely on other dependencies themselves. A quick look in our Node modules folder quickly exposes the web of complexity within, which few of us can really fathom. A single point of failure can bring down the entire stack, like the well-worn “house of cards” metaphor.</p>
<p>Complexity isn’t the only issue, however. More dependencies often result in a bigger Javascript payload delivered to the user, with an accompanying negative impact on performance and <a href="https://blog.webpagetest.org/posts/carbon-control/">the environment</a>.</p>
<figure>
<img srcset="https://css-irl.info/reducing-complexity-01.jpg 1800w, https://css-irl.info/reducing-complexity-01_1200.jpg 1200w, https://css-irl.info/reducing-complexity-01_800.jpg 800w" sizes="(min-width: 1200px) 1200px, 90vw" src="https://css-irl.info/reducing-complexity-01_1200.jpg" width="1200" height="829" alt="Screenshot of Bundlephobia analysis of create-react-app, showing total bundle size and download time of 2.85 seconds on slow 3G" />
<figcaption>A <a href="https://bundlephobia.com/">Bundlephobia</a> analysis of create-react-app shows a total package size of 492.5kB and 11 other dependencies.</figcaption>
</figure>
<h2>Radical solutions</h2>
<p>One of Jack’s more radical suggestions is to commit our Node modules to the repository. (You could almost hear the collective gasp of the developers on the room at this point in the talk.) But as he breaks down the reasoning, it makes more and more sense. Not only does this provide increased visibility of the size of our dependency tree (so we can’t fail to be aware of our application’s complexity), it means that we can skip the installation step. If something happens to a public package, we have our own copy <strong>that we know works</strong>.</p>
<p>I can certainly think of a few downsides of this approach, but just maybe the positives outweigh the negatives? Updating our packages would become more of a conscious task, giving us the opportunity to assess whether we still need them.</p>
<h2>Use the platform</h2>
<p>Jack envisages a time when we can vastly simplify our JS stack — if not eradicating third-party dependencies completely, leaning on the web platform, and ensuring external dependencies are carefully considered and regularly evaluated. If this sounds like a distant dream, it’s worth remembering that just a few short years ago, this was in fact the case.</p>
<p>It’s only relatively recently that we’ve had all the world’s code virtually at our fingertips, and since then the web platform has become infinitely more capable. Just look at the plethora of new CSS features released in recent years that place more power in the hands of developers: container queries, <code>:has()</code>, colour functions, cascade layers and much more. We’re even on the cusp of getting native <a href="https://developer.chrome.com/docs/web-platform/view-transitions/">view transitions</a>, something that was previously the preserve of JS frameworks.</p>
<p>Yes, we expect more for our web apps today that we did a few years ago. But many would argue that the increase in functionality hasn’t necessarily translated to a better user experience, as web pages became more bloated.</p>
<h2>When to evaluate third-party code</h2>
<h3>New projects</h3>
<p>Starting a new project always feels like a great opportunity to streamline and keep things clean, including our dependency tree. But over time, complexity inevitably creeps in if we’re not careful. So it’s worth carefully assessing dependencies as and when we feel we need them: whether they provide all the benefits we need, where they might fall short, how much time they will save us compared to writing our own, or whether they are too heavy-handed for our needs. Sometimes the answer is “yes, we still need them”! But it should be deliberate choice.</p>
<h3>Old projects</h3>
<p>Legacy projects are harder, as they already likely come with a lot of baggage. Working out what is and isn’t needed can be headache.</p>
<p>When we encounter bugs with existing dependencies, we could treat it as an opportunity to consider whether that dependency is still necessary. I recently encountered a bug with modal dialogues on a web app I was working on. It turned out to be the result of the package I had been using to handle modal accessibility (because this stuff is hard!), which hadn’t been maintained in over a year. Rather than attempting to find a replacement, or code up a solution myself, it seems a great time to replace this with the native HTML <code><dialog></code> element, which is now well-supported.</p>
<p>This isn’t going to be natural conclusion for everybody: <code><dialog></code> only gained support in Safari in 2022, meaning there are likely to be a proportion of users stuck on legacy versions of that browser for whom this will not work. But there is a <a href="https://github.com/GoogleChrome/dialog-polyfill">polyfill</a>, which will likely be easier to remove once there is a satisfactory amount of support among your user base than a fully-featured library. The important thing is to evaluate on a case-by-case basis. (Read Scott O’Hara’s article <a href="https://www.scottohara.me/blog/2023/01/26/use-the-dialog-element.html">Use the dialog element (reasonably)</a> for more info.)</p>
<h2>New platform features</h2>
<p>These are some other web platform features I’ve been able to replace old libraries with.</p>
<h3>:focus-visible</h3>
<p><code>:focus-visible</code> now has wide browser support, and support can be detected with CSS is order to provide a fallback, meaning that the polyfill I had previously used could be removed.</p>
<h3>Custom properties</h3>
<p>I’m currently working on an old project that contains a lot of redundant code, and is also a huge pain to even get up and running. Not wanting to add complexity to what was already an unwieldy codebase, I elected to eschew a build step and style with vanilla CSS. Modern CSS features have made this way more viable than a few years ago, but custom properties especially are a life-saver. They effectively replace Sass variables, and can do so much more besides.</p>
<h3>scroll-behavior</h3>
<p>Often when a user clicks an in-page link, we want the page to smoothly scroll to the appropriate point. This used to be the preserve of Javascript, but now we can do it easily in CSS with <code>scroll-behavior: smooth</code>.</p>
<pre class="language-css"><code class="language-css"><span class="token comment">/* Only apply smooth scrolling when the user hasn't set their motion preference to "reduce" */</span>
<span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">prefers-reduced-motion</span><span class="token punctuation">:</span> no-preference<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token selector">html</span> <span class="token punctuation">{</span>
<span class="token property">scroll-behavior</span><span class="token punctuation">:</span> smooth<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token comment">/* Offset the element scrolled to */</span>
<span class="token selector">section</span> <span class="token punctuation">{</span>
<span class="token property">scroll-margin-top</span><span class="token punctuation">:</span> 5rem<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>We can also initiate smooth scrolling to a specified position <a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollTo">in JS</a>, without the need for a library:</p>
<pre class="language-js"><code class="language-js">element<span class="token punctuation">.</span><span class="token function">scrollTo</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token literal-property property">top</span><span class="token operator">:</span> <span class="token number">200</span><span class="token punctuation">,</span>
<span class="token literal-property property">left</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span>
<span class="token literal-property property">behavior</span><span class="token operator">:</span> <span class="token string">'smooth'</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<h3>Intersection Observer</h3>
<p>Animating elements as they scroll into view can add an elegant touch to a web page. Thanks to the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API">Intersection Observer API</a>, we can detect when an element arrives or leaves the viewport without reaching for heavy JS libraries.</p>
<h2>Think before you <code>npm install</code></h2>
<p>This isn’t to say that <strong>all</strong> library features can easily be replaced. We’ll still likely be importing third-party code for some time. But instead of doing it automatically every time, let’s take the time to pause and consider whether we really need to.</p>
<p><a href="https://heypresents.com/talks/abstractions-complexities-and-off-ramps">Watch Jack Franklin’s talk from All Day Hey→</a></p>
Carbon Capture: A New Way to Measure Your Website’s Carbon Emissions2023-05-16T00:00:00Zhttps://css-irl.info/carbon-capture-a-new-way-to-measure-carbon-emissions/<figure>
<img src="https://css-irl.info/carbon-capture-01.jpg" alt="Screenshot of WebPageTest Carbon Capture homepage" width="1600" height="900" srcset="https://css-irl.info/carbon-capture-01.jpg 1800w, https://css-irl.info/carbon-capture-01_1200.jpg 1200w, https://css-irl.info/carbon-capture-01_800.jpg 800w" sizes="(min-width: 1086px) 75vw, (min-width: 1264px) 930px, 90vw" />
</figure>
<p><a href="https://www.webpagetest.org/">WebPageTest</a> is a great tool for measuring your website’s performance, providing detailed metrics and actionable advice. They’ve just released a brand new add-on, Carbon Capture, which measures the CO2 emissions generated by a website. It includes detailed breakdowns on which resources have the greatest carbon impact, as well as quantifying the total emissions. Read all about it in this blog post by <a href="https://blog.webpagetest.org/posts/carbon-control/">Scott Jehl at WebPageTest</a>.</p>
<p>This is awesome in terms of providing more visibility to developers on the environmental impact of the products we build. I hope it will help provide greater incentives for debt and their clients to prioritise performance and sustainability. Perhaps in the not too distant future we’ll see these metrics as a standard part of developer tools, making carbon awareness fully mainstream.</p>
<p>We should also remember these are estimated emissions, using our best available resources for measuring our websites carbon emissions right now, as Fershad writes in his excellent article <a href="https://heypresents.com/talks/building-a-greener-web">Is data transfer the best proxy for website carbon emissions?</a>. But hopefully it won’t be too long before we see these tools proving a more accurate picture.</p>
<h2>Video: Building a Greener Web</h2>
<p>I gave a talk at All Day Hey conference recently about why this stuff matters, and some practical steps we as developers can take to reduce the environmental impact of the products we build. <a href="https://heypresents.com/talks/building-a-greener-web">Watch it here</a> to learn more.</p>
Useful Resources for Improving Your Site’s Performance (and Reducing Carbon Emissions)2023-05-01T00:00:00Zhttps://css-irl.info/useful-resources-for-web-performance/<p>There are lots of areas where better web performance and reducing carbon emissions overlap: faster websites are often less carbon-intensive ones. They transfer less data, cache efficiently, and make fewer requests, all of which reduce energy consumption.</p>
<p>I’ve been consolidating a bunch of resources for an upcoming talk on Building a Greener Web. Here are a few resources you might find useful for building faster (and greener) websites.</p>
<h2>Reducing the Site-Speed Impact of Third-Party Tags</h2>
<p>An article by web performance consultant <a href="https://andydavies.me/">Andy Davies</a> on the performance impact of third party scripts and tags, and what we can do to mitigate them. I’ve used many of the tips in the article, with very successful results.</p>
<p><a href="https://andydavies.me/blog/2020/10/02/reducing-the-site-speed-impact-of-third-party-tags/">Read the article</a></p>
<h2>How To Convert Variable TTF Font Files to WOFF2</h2>
<p>Variable fonts can be great for performance. They enable us to load multiple weights and styles of a particular font from a single font file. Unfortunately a lot of them are only available as TTF files, which tend to be significantly larger than WOFF2, despite WOFF2 being well-supported. <a href="https://henry.codes/">Henry DesRoches</a> details how to use Google’s <a href="https://github.com/google/woff2.git">WOFF2 converter</a> library to convert variable fonts from TTF to WOFF2.</p>
<p><a href="https://henry.codes/writing/how-to-convert-variable-ttf-font-files-to-woff2/">Read the article</a></p>
<h2>Cache Control for Civilians</h2>
<p>If, like me, your knowledge of caching is pretty minimal, you could do worse than check out this article on cache-control headers by <a href="https://csswizardry.com/">Harry Roberts</a>. You’ll soon know your <code>no-cache</code> from your <code>must-revalidate</code>.</p>
<p><a href="https://csswizardry.com/2019/03/cache-control-for-civilians/">Read the article</a></p>
Exploring :has() Again2023-04-16T00:00:00Zhttps://css-irl.info/exploring-has-again/<p>It’s been a while since my last article on here (well, a month is a while for me, anyway 😅), as I’ve been busy with my head down preparing for my talk on modern CSS layout at <a href="https://beyondtellerrand.com/events/dusseldorf-2023">Beyond Tellerand</a>, which is (<strong>checks notes</strong>) tomorrow!</p>
<p>Although I haven’t had much time for blogging, it has given me a chance to delve once again into messaging about with CSS layout and the <code>:has()</code> pseudo-class, or “parent selector”. I’ve already written about <code>:has()</code> in a <a href="https://css-irl.info/animated-grid-tracks-with-has/">previous post</a>, and you can read an in-depth guide by Jen Simmons on <a href="https://webkit.org/blog/13096/css-has-pseudo-class/">the Webkit blog</a>. But to recap, <code>:has()</code> allows us to select a parent or sibling of an element by taking a relative selector list as its argument (to paraphrase <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:has">MDN</a>).</p>
<p>But what I’m realising as I continue to play around with <code>:has()</code> is that in practice, we can select <strong>any</strong> element in the DOM relative to another, as long as they share a common ancestor — which they should, right? As any element that we want to select is going to have the <code><body></code> as its ancestor. This makes it pretty powerful.</p>
<p>So here a couple of demos to that effect. (You’ll need to view these in a browser that supports <code>:has()</code>, e.g. Chrome or Safari.) In this one, hovering on a list item on the left animates our grid tracks on the right. They share a common wrapper element as their ancestor, but they don’t necessarily have to.</p>
<p class="codepen" data-height="483.28997802734375" data-default-tab="result" data-slug-hash="vYzqaNO" data-user="michellebarker" style="height: 483.28997802734375px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/vYzqaNO">
Animated grid tracks with :has()</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>In this second demo, we’re adding a background colour to the <code><section></code> element targeted by a link in another <code><section></code> on hover or click. This is perhaps a bit of a silly demo, but I can see some potential use cases, such as providing a visual clue for users as to the targeted section of a document.</p>
<p class="codepen" data-height="475.6033020019531" data-default-tab="result" data-slug-hash="xxyVNOz" data-user="michellebarker" data-token="9fb466159ce1904301e96f319e7a42c6" style="height: 475.6033020019531px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/xxyVNOz/9fb466159ce1904301e96f319e7a42c6">
Untitled</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>Lastly, this demo (best viewed on desktop) opens a panel (using animated grid tracks) when the user clicks the link on the left. The link’s target is hidden within the collapsed panel. (It also combines container queries for the content on the right.) Hiding and revealing content is often something that should be done with JS, and there are likely some accessibility issues here — I’m not suggesting you should do anything like this in production. But it’s interesting to see the power of CSS today!</p>
<p class="codepen" data-height="490.4861145019531" data-default-tab="result" data-slug-hash="XWPNqyP" data-user="michellebarker" style="height: 490.4861145019531px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/XWPNqyP">
Animated grid tracks, :has() + container queries</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
Creating VS Code Snippets to Speed Up Workflow2023-03-07T00:00:00Zhttps://css-irl.info/creating-vscode-snippets-to-speed-up-workflow/<p>Writing code can be repetitive, and many developers (myself included) opt to make our lives easier by configuring our code editor of choice to auto-complete common statements in the given coding language.</p>
<p>My own preferred editor, <a href="https://code.visualstudio.com/">VS Code</a>, includes <a href="https://code.visualstudio.com/docs/editor/intellisense">Intellisense</a> out of the box, which provides common code snippets for auto-completion as you type. Pressing <kbd>Enter</kbd> or <kbd>Tab</kbd> inserts them into your code.</p>
<p>Perhaps you need something more custom? There are many extensions in the VS Code Marketplace that provide snippets in a variety of languages. Personally, I use Sarah Drasner’s <a href="https://marketplace.visualstudio.com/items?itemName=sdras.vue-vscode-snippets">Vue VS Code Snippets</a> extension, which includes a whole bunch of snippets for Vue.</p>
<h2>Writing our own snippets</h2>
<p>But we can also create our own snippets! To try this out, let’s create a snippet for a min-width media query — something I have to type out fairly frequently.</p>
<h3>Create a new snippets file</h3>
<p>To access the application snippets files we can press <kbd>Shift</kbd> + <kbd>Command</kbd> + <kbd>P</kbd> to bring up the command palette on a Mac (on Windows it might be a slightly different combination), then select the option <strong>Configure User Snippets</strong>. Alternatively, we can go to <strong>Code > Settings > Configure User Snippets</strong>. This brings up any existing snippets files, and a menu of available languages for creating new snippets. We can select one of these, or select <strong>New Global Snippets file</strong> if our snippet should be available in all languages. We’ll select “CSS”. This brings up a new JSON file, where we can start writing our snippet.</p>
<p>There’s also an option to create project-specific snippets. I generally find creating global ones most useful, but there could be occasions where project-specific snippets might be preferred. You could commit this to the repository to share it between all developers.</p>
<h3>Define the snippet</h3>
<p>Let’s add some JSON for our new snippet, which we’re going to call “Breakpoint”. Our code needs to include three things:</p>
<ol>
<li>A prefix — what we’ll type in order to trigger the snippet to be inserted</li>
<li>The body — the actual code to be inserted</li>
<li>A description — this appears in the menu, and will help us find our snippet easily.</li>
</ol>
<p>We’ll create a simple, single-line snippet first:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
<span class="token property">"Breakpoint"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"prefix"</span><span class="token operator">:</span> <span class="token string">"bp"</span><span class="token punctuation">,</span>
<span class="token property">"body"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"@media screen and (min-width: )"</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token property">"description"</span><span class="token operator">:</span> <span class="token string">"Create a new min-width media query"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>Now, if we open a CSS file in VS Code and type <code>bp</code> then <kbd>Enter</kbd>, the body of our snippet should be inserted into our file!</p>
<h3>Multi-line snippets</h3>
<p>If we want to include multiple lines of code, we need to write each line as a string in the body array. We’ll make our snippet span three lines, adding the curly parentheses:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
<span class="token property">"Breakpoint"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"prefix"</span><span class="token operator">:</span> <span class="token string">"bp"</span><span class="token punctuation">,</span>
<span class="token property">"body"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"@media screen and (min-width: ) {"</span><span class="token punctuation">,</span> <span class="token string">""</span><span class="token punctuation">,</span> <span class="token string">"}"</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token property">"description"</span><span class="token operator">:</span> <span class="token string">"Create a new min-width media query"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<h3>Cursor position and tab stops</h3>
<p>We can control the position of the cursor when the snippet is expanded. Adding <code>$0</code> into a string denotes the final tab stop, while <code>$1</code>, <code>$2</code>, etc. are cursor positions that can be tabbed through in order.</p>
<p>As the first thing we’ll probably want to do is determine the min-width for the media query, let’s set our first tab stop there. We’ll set our final cursor position inside the media query parentheses, with a tab character before it (<code>\t</code>)</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
<span class="token property">"Breakpoint"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"prefix"</span><span class="token operator">:</span> <span class="token string">"bp"</span><span class="token punctuation">,</span>
<span class="token property">"body"</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token string">"@media screen and (min-width: $1) {"</span><span class="token punctuation">,</span> <span class="token string">"\t$0"</span><span class="token punctuation">,</span> <span class="token string">"}"</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token property">"description"</span><span class="token operator">:</span> <span class="token string">"Create a new min-width media query"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<h3>Placeholder choice</h3>
<p>If we have a few breakpoint values we use fairly frequently, we could configure our snippet to allow us to select from those predefined values. Let’s add three possible values to choose from for our media query:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
<span class="token property">"Breakpoint"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"prefix"</span><span class="token operator">:</span> <span class="token string">"bp"</span><span class="token punctuation">,</span>
<span class="token property">"body"</span><span class="token operator">:</span> <span class="token punctuation">[</span>
<span class="token string">"@media screen and (min-width: ${1|600px,900px,1200px|}) {"</span><span class="token punctuation">,</span>
<span class="token string">"\t$0"</span><span class="token punctuation">,</span>
<span class="token string">"}"</span>
<span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token property">"description"</span><span class="token operator">:</span> <span class="token string">"Create a new min-width media query"</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<h2>Using our snippet</h2>
<p>Now when we type <code>bp</code> in a CSS file, it should expand our media query and give us a choice of values, which we can select by pressing <kbd>Enter</kbd>. We can also opt to insert a snippet by bringing up the command palette (<kbd>Shift</kbd> + <kbd>Command</kbd> + <kbd>P</kbd>) and selecting <strong>Snippets: Insert Snippet</strong>, which brings up relevant snippets for our file.</p>
<h2>Further reading</h2>
<p>VS Code snippets are great for speeding up your workflow. There’s even more you can do with them, including using variables and assigning key bindings. Check out the <a href="https://code.visualstudio.com/docs/editor/userdefinedsnippets#_create-your-own-snippets">documentation</a>. Happy coding!</p>
A Native “Visually Hidden” in CSS? Yes Please!2023-03-01T00:00:00Zhttps://css-irl.info/a-native-visually-hidden-class/<p>If you’ve been writing CSS for any length of time, the chances are you’ll have come across situations where you need to hide some text visually, but still have that text available to assistive technologies (such as screenreaders). <a href="https://www.sarasoueidan.com/blog/accessible-icon-buttons/">Icon buttons</a> and <a href="https://css-tricks.com/how-to-create-a-skip-to-content-link/">skip links</a> are just two examples. (There are plenty more.) Simply using <code>display: none</code> hides text from assistive technologies, which is not useful to us in this scenario. So developers over the years have come up with clever ways to hide text visually, which usually manifest as a utility class that gets copied and pasted into every project, often called <code>.visually-hidden</code> or <code>.sr-only</code> — something like:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.sr-only</span> <span class="token punctuation">{</span>
<span class="token property">border</span><span class="token punctuation">:</span> 0 <span class="token important">!important</span><span class="token punctuation">;</span>
<span class="token property">clip</span><span class="token punctuation">:</span> <span class="token function">rect</span><span class="token punctuation">(</span>1px<span class="token punctuation">,</span> 1px<span class="token punctuation">,</span> 1px<span class="token punctuation">,</span> 1px<span class="token punctuation">)</span> <span class="token important">!important</span><span class="token punctuation">;</span>
<span class="token property">-webkit-clip-path</span><span class="token punctuation">:</span> <span class="token function">inset</span><span class="token punctuation">(</span>50%<span class="token punctuation">)</span> <span class="token important">!important</span><span class="token punctuation">;</span>
<span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token function">inset</span><span class="token punctuation">(</span>50%<span class="token punctuation">)</span> <span class="token important">!important</span><span class="token punctuation">;</span>
<span class="token property">height</span><span class="token punctuation">:</span> 1px <span class="token important">!important</span><span class="token punctuation">;</span>
<span class="token property">overflow</span><span class="token punctuation">:</span> hidden <span class="token important">!important</span><span class="token punctuation">;</span>
<span class="token property">margin</span><span class="token punctuation">:</span> -1px <span class="token important">!important</span><span class="token punctuation">;</span>
<span class="token property">padding</span><span class="token punctuation">:</span> 0 <span class="token important">!important</span><span class="token punctuation">;</span>
<span class="token property">position</span><span class="token punctuation">:</span> absolute <span class="token important">!important</span><span class="token punctuation">;</span>
<span class="token property">width</span><span class="token punctuation">:</span> 1px <span class="token important">!important</span><span class="token punctuation">;</span>
<span class="token property">white-space</span><span class="token punctuation">:</span> nowrap <span class="token important">!important</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>It’s so common that even popular CSS frameworks like <a href="https://tailwindcss.com/">Tailwind</a> come with built-in <code>.sr-only</code> classes.</p>
<p>But as Ben Myers points out in this excellent article on <a href="https://benmyers.dev/blog/native-visually-hidden/">why the web needs this as a native feature</a>, there are problems with this. One issue is that there are many variations on this snippet, which have been found to cause problems as browsers and have evolved over time. Ben argues that it’s about time this became a native feature, and I absolutely agree. In the article, Ben outlines some of the advantages and drawbacks to the various forms this might take, such as a value of the <code>display</code> property, a property all of its own, or a HTML attribute. I recommend giving it a read, and hopefully something will come out of it 🙂</p>
<p><a href="https://benmyers.dev/blog/native-visually-hidden/">Read the article</a></p>
Setting Up a New(ish) MacBook2023-02-28T00:00:00Zhttps://css-irl.info/setting-up-a-newish-macbook/<figure>
<img src="https://css-irl.info/setting-up-a-new-macbook.svg" alt="Digital illustration of a MacBook in bright colours, with the words “let’s go!” on the screen" width="1600" height="900" />
</figure>
<p>I recently dusted off a relatively old (~5 years) MacBook and replaced the battery with the plan that I could use it as a secondary machine, for my “non-work” stuff. The last couple of times I’ve got a new Mac I’ve gone for the option of cloning my old setup, so I don’t need to install everything again. This time, however, the whole point was to keep things simple, minimal and (hopefully) fast, so I decided to completely wipe it and start from scratch.</p>
<p>If you’re a developer, naturally there’s a bunch of stuff you need to install to get your environment perfectly right for coding. The following is the process I went through to install the things I need for my setup, written purely to remind me what I did, when I inevitably have to do it again sometime.</p>
<h2>1. Install VSCode</h2>
<p>When you’re coding (or indeed, installing anything you need to write code), what’s the first thing you need? A terminal! Sure, I could use built-in one on my MacBook, or download <a href="https://iterm2.com/">iTerm</a>. But I generally use VS Code’s terminal, and since I’ll <strong>definitely</strong> need VS Code for, well, writing code, let’s <a href="https://code.visualstudio.com/">go ahead and install it right away</a>.</p>
<h3><code>code</code> command</h3>
<p>In VS Code we can open a project from the command line by navigating to the project and typing <code>code .</code>. On a Mac, we first need to enable this by running <code>Install 'code' command in PATH</code>. (See the VS Code <a href="https://code.visualstudio.com/docs/editor/command-line">command line docs</a> for more details)</p>
<h2>2. Use zsh as the default</h2>
<p>As of macOS 10.15, the default shell is zsh. However, VS Code automatically opens a bash terminal. <a href="https://dev.to/nyanev/how-to-use-zsh-in-vs-code-for-mac-39dp">How to fix this</a>? We need to go into our settings in VS Code, find the property <code>terminal.integrated.shell.osx</code> and change this to <code>zsh</code>.</p>
<p>We’ll also need to create a <em>.zshrc</em> file by running <code>touch ~/.zshrc</code>. This is a hidden file, so it won’t show up in Finder by default. To <a href="https://www.techradar.com/how-to/how-to-show-hidden-files-in-macos">view hidden files</a> we can run:</p>
<pre class="language-bash"><code class="language-bash">defaults <span class="token function">write</span> com.apple.Finder AppleShowAllFiles <span class="token boolean">true</span></code></pre>
<p>Then close and reopen Finder, and our hidden files should be visible. Now we can open this file in VS Code.</p>
<h2>3. Xcode Command Line Tools (and Git)</h2>
<p>For the next steps we’re going to need <a href="https://www.freecodecamp.org/news/install-xcode-command-line-tools/">Xcode Command Line Tools</a>. We can download Xcode from the App Store, or use this command.</p>
<pre class="language-bash"><code class="language-bash">xcode-select <span class="token parameter variable">--install</span></code></pre>
<p>That gives us a prompt to confirm we want to install the command line developer tools. Yes, we do. As a special bonus, this process also installs <a href="https://git-scm.com/book/en/v2/Getting-Started-Installing-Git">Git</a>, which we’re definitely going to need, so that’s handy.</p>
<h2>4. Install Oh My ZSH plugin</h2>
<p><a href="https://github.com/ohmyzsh/ohmyzsh">Oh My Zsh</a> is an open source framework for managing your zsh configuration. It enables us to easily install plugins, change our shell theme, and more. Let’s follow the instructions in the Github repository and install it via the command line:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">sh</span> <span class="token parameter variable">-c</span> <span class="token string">"<span class="token variable"><span class="token variable">$(</span><span class="token function">curl</span> <span class="token parameter variable">-fsSL</span> https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh<span class="token variable">)</span></span>"</span></code></pre>
<h2>5. NVM</h2>
<p>I can pretty much guarantee I’m going to need Node installed for <strong>anything</strong> I’m working on. I like to use <a href="https://github.com/nvm-sh/nvm">NVM</a> (Node Version Manager), which makes it super easy to install and switch between Node versions. The best way I’ve found to install this is using the <a href="https://github.com/lukechilds/zsh-nvm">zsh-nvm</a> plugin for Oh My Zsh.</p>
<p>First we clone the repository to our zsh plugins directory:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">git</span> clone https://github.com/lukechilds/zsh-nvm ~/.oh-my-zsh/custom/plugins/zsh-nvm</code></pre>
<p>Then add it to our plugins section of the <em>.zshrc</em> file. We can list plugins like this:</p>
<pre><code>plugins=(
git
zsh-nvm
)
</code></pre>
<p>Now we should have NVM installed. We can run <code>nvm -v</code> to check.</p>
<h2>6. Z plugin</h2>
<p>Another plugin I find really useful is <a href="https://github.com/agkozak/zsh-z">Zsh-z</a>, which enables us to jump to commonly-used directories simply by typing <code>z</code> followed by the directory name. We don’t actually need to install it, as it’s now included as part of Oh My Zsh, so we can just add it to our plugins in the <em>.zshrc</em> file:</p>
<pre><code>plugins=(
git
zsh-nvm
z
)
</code></pre>
<h2>7. Setting up aliases</h2>
<p>Finally, I like to set up a few aliases for the terminal commands I use the most. We can add these to our <code>.zshrc</code> file.</p>
<pre><code>alias gpum='git pull origin main'
alias gpm='git push origin main'
alias ga="git add ."
alias gm='git commit -m'
</code></pre>
<p>Of course, you can add any other aliases and plugins you like, as well as change your terminal theme, if that’s your bag. Happy coding!</p>
Resizing with CSS2023-02-17T00:00:00Zhttps://css-irl.info/resizing-with-css/<p>In case you missed it, <a href="https://web.dev/cq-stable/">container queries landed in all stable browsers</a> this week! It’s a pretty exciting time to be working with CSS, seeing this long-awaited feature finally become useable.</p>
<p>Una’s post on <a href="https://web.dev/cq-stable/">web.dev</a> includes a rundown of how to use container queries, as well as some nifty demos. By coincidence, I’ve also been editing a soon-to-be-published article by <a href="https://thinkdobecreate.com/">Stephanie</a> on container queries, which also includes some rather lovely demos. Something I’ve noticed about both sets of demos is that they make use of the <code>resize</code> CSS property to create a resizeable area within the document, rather than relying on the user having to resize their entire viewport to see the container queries in action.</p>
<p class="codepen" data-height="560" data-default-tab="result" data-slug-hash="ZEMzNGj" data-user="web-dot-dev" style="height: 560px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/web-dot-dev/pen/ZEMzNGj">
Untitled</a> by web.dev (<a href="https://codepen.io/web-dot-dev">@web-dot-dev</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>I was vaguely aware of the <code>resize</code> property, but I hadn’t really thought about any use case for it before, so it kind of passed me by. But it seems perfect for this! I’ll definitely keep it in mind for future demos. You can select which direction to apply the resizing: <code>horizontal</code>, <code>vertical</code> or <code>both</code>, as well as logical properties <code>inline</code> and <code>block</code>, with <code>none</code> as the default.</p>
<pre class="language-css"><code class="language-css"><span class="token comment">/* Makes the element horizontally resizeable */</span>
<span class="token selector">div</span> <span class="token punctuation">{</span>
<span class="token property">resize</span><span class="token punctuation">:</span> horizontal<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>The element must be a scroll container, meaning that it must have its <code>overflow</code> property value set to <code>scroll</code> or <code>auto</code> in order for <code>resize</code> to have any effect.</p>
<h2>Use cases</h2>
<p>Besides these container query demos, I guess another scenario where it might be handy is if you have a floating toolbar or some user controls, and you want the user to be able to move these around the page and position them however they want. Another possible use case could be a long list of options in a scrollable container: the user could resize the element vertically to see more of the options and reduce the amount the need to scroll.</p>
<p class="codepen" data-height="497" data-default-tab="result" data-slug-hash="QWVbzMX" data-user="michellebarker" style="height: 497px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/QWVbzMX">
Untitled</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>A kind of cool thing here is that we can set a <code>max-height</code> too, permitted the user to resize only up to a point if we so choose.</p>
<p><code>resize</code> also respects flex and grid layouts. In this demo the user is permitted to resize the navigation column and the main content area will adapt, as we’re using <code>1fr</code> for the grid column on the right, which causes it to fill the available space.</p>
<p class="codepen" data-height="423" data-default-tab="result" data-slug-hash="eYLNepQ" data-user="michellebarker" style="height: 423px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/eYLNepQ">
Resize</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<h2>Limitations</h2>
<p>One drawback is the resize control is only positioned in the bottom right corner of the element, and it’s not possible to style it. It would be nice to have this customiseable ability, and increase the area of the resize control. In the menu example it would be better if the user could grab <strong>any</strong> part of the right hand edge, for example.</p>
<p>In any case, <code>resize</code> will feature much more highly on my radar from now on.</p>
<ul>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/resize">Read about it on MDN</a></li>
<li><a href="https://w3c.github.io/csswg-drafts/css-ui/#resize">Read the spec</a></li>
</ul>
Working with Colour Scales for Data Visualisation in D32023-02-14T00:00:00Zhttps://css-irl.info/working-with-color-scales-for-data-visualisation-in-d3/<figure>
<img src="https://css-irl.info/working-with-color-scales-03.jpg" alt="Temperature chart with threshold scale applied, showing distinct colour bands" width="1200" height="578" />
<figcaption>Temperature anomaly data from the Global Warming and Climate Change API, visualised with D3</figcaption>
</figure>
<p>There are many different ways of using colour informatively when it comes to data visualization. But we also need to ensure our use of colour conveys the right information, and that it is used accessibly. In this article we’ll walk through some examples of using colour in different types of visualizations with the data visualization library <a href="https://d3js.org/">D3.js</a>. Some familiarity with D3 will be assumed, but there will be some helpful pointers regardless of how you create your visualizations.</p>
<h2>Is colour the best option?</h2>
<p>Colour can be extremely useful in conveying meaning in our designs. It can also be difficult to get right, and in the worst case could have a negative impact on the user experience. Some colour combinations are difficult to perceive by people with colour blindness or low vision. Some colours might have different cultural significance in different parts of the world, which can affect how they are interpreted in a UI. The colour red might signify warning or danger in the western world, but in East Asian countries often represents happiness and good fortune.</p>
<p>Therefore, it’s rarely a good idea to rely on colour alone. Before creating a chart, we should consider what other options are available to us for visualizing our data. It’s useful to keep in mind <a href="https://webflow.com/blog/gestalt-principles-of-design">Gestalt principles</a>: size, position, proximity, grouping and contrast could be some other ways to differentiate data, as well as text labels when necessary. On the web we’re lucky because our charts can also be interactive: we can progressively disclose more information to the user, without overwhelming them.</p>
<h2>Colour scales</h2>
<p>Assuming we’ve established that colour is important for our visualization, let’s look at some colour scale options. We’re going to be using the <a href="https://github.com/d3/d3-scale">d3-scale</a> module, and evaluating some different use cases for its various scale functions.</p>
<h3>Overview of scales</h3>
<p>The purpose of a scale is to map one range of values to another. In D3 (and data visualization in general), their purpose is to map values in a set of data to a visual representation. We can see the principle illustrated in <a href="https://codepen.io/michellebarker/pen/PoEGXjM">this demo</a>, where hovering on the larger area maps the position on the smaller one.</p>
<p class="codepen" data-height="500" data-default-tab="result" data-slug-hash="PoEGXjM" data-user="michellebarker" style="height: 500px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/PoEGXjM">
D3 domain/range</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>The input (that is, the data we want to map) is the <strong>domain</strong> and the output (consisting of the range of values our mapped data should result in) is called the <strong>range</strong>.</p>
<p>D3 has a lot of different scale functions, but we’ll look at a handful that are particularly useful when working with colour in data visualization. To demonstrate we’re going to visualize data from the Global Warming and Climate Change API. To start with we’ll fetch data from <a href="https://global-warming.org/api/temperature-api">this endpoint</a>, which gives us an array of global temperature anomalies from 1893 to the present day. We’ll plot these as a linear gradient in CSS, which will allow us to see the advantages on disadvantages of the different types of scales in action.</p>
<p>To help visualise the result of applying different colour scales, here is an interactive demo that demonstrates linear, diverging and threshold colour scales — the main ones I’ll be explaining in this article.</p>
<p class="codepen" data-height="489" data-default-tab="result" data-slug-hash="NWBZQRj" data-user="michellebarker" style="height: 489px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/NWBZQRj">
D3 color scale visualizer</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<h2>Continuous scales</h2>
<p><a href="https://github.com/d3/d3-scale#continuous-scales">Continuous scales</a> in D3 enable us to map a continuous, quantitative input domain to a continuous output range. Let’s look at an example of a continuous colour scale using D3’s <code>scaleLinear</code> function. Here we’re creating a function to map a domain of values from 0 to 100 to a continuous colour range from royalblue to pink.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> colorScale <span class="token operator">=</span> d3
<span class="token punctuation">.</span><span class="token function">scaleLinear</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">domain</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">range</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">'royalblue'</span><span class="token punctuation">,</span> <span class="token string">'pink'</span><span class="token punctuation">]</span><span class="token punctuation">)</span></code></pre>
<p>If we call our colour scale function, passing in a value from 0 to 100 we’ll get a colour that falls somewhere within that range. So <code>colorScale(0)</code> will give us royalblue (output as <code>rgb(173, 216, 230)</code>), <code>colorScale(100)</code> will give us pink (<code>rgb(255, 192, 203)</code>), while <code>colorScale(50)</code> results in a lavender colour (interpolated from blue and pink).</p>
<figure>
<img src="https://css-irl.info/working-with-color-scales-02.jpg" alt="Screenshot showing the resulting colours on the colour scale" width="1035" height="292" />
</figure>
<h3>Clamping</h3>
<p>Passing in a value outside of our expected domain can result in some unexpected colours. For example, <code>colorScale(200)</code> results in <code>rgb(255, 255, 181)</code>, a pale yellow colour!</p>
<figure>
<img src="https://css-irl.info/working-with-color-scales-01.svg" alt="Colour swatches showing the (expected) colour values for 0 and 100, and the unexpected yellow colour for 200" width="1600" height="900" />
</figure>
<p>A solution is to use the clamp method, which ensures any values outside of the domain will be clamped to the given range, and we won’t get any unexpected results.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> colorScale <span class="token operator">=</span> d3
<span class="token punctuation">.</span><span class="token function">scaleLinear</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">domain</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">range</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">'royalblue'</span><span class="token punctuation">,</span> <span class="token string">'pink'</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">clamp</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span></code></pre>
<h2>Working with data</h2>
<p>When working with data it’s likely we’ll want our domain to be determined by the data itself. We can use D3’s <code>extent</code> function to get the highest and lowest values in our dataset as an array. We can pass in a simple array, for example:</p>
<pre class="language-js"><code class="language-js">d3<span class="token punctuation">.</span><span class="token function">extent</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">20</span><span class="token punctuation">,</span> <span class="token number">50</span><span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token comment">// result: [0, 100]</span></code></pre>
<p>Alternatively we can use an accessor function, if we want to use a particular attribute from our data. Let’s fetch some data from the Global Warming and Climate Change API and use the <code>extent</code> function to get the domain based on land temperature anomaly values from the dataset. We’ll write a new function called <code>draw</code> to do this, seeing as we’ll be using it quite a bit later on:</p>
<pre class="language-js"><code class="language-js"><span class="token comment">/* Fetch data */</span>
<span class="token function">fetch</span><span class="token punctuation">(</span><span class="token string">'https://global-warming.org/api/temperature-api'</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">response</span><span class="token punctuation">)</span> <span class="token operator">=></span> response<span class="token punctuation">.</span><span class="token function">json</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">response</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> data <span class="token operator">=</span> <span class="token function">downsampleData</span><span class="token punctuation">(</span>response<span class="token punctuation">.</span>result<span class="token punctuation">)</span>
<span class="token function">draw</span><span class="token punctuation">(</span>data<span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">catch</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">error</span><span class="token punctuation">)</span> <span class="token operator">=></span> error<span class="token punctuation">)</span>
<span class="token comment">/* Accessor function to get the land temperature from the datum */</span>
<span class="token keyword">const</span> <span class="token function-variable function">yAccessor</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">d</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token function">parseFloat</span><span class="token punctuation">(</span>d<span class="token punctuation">.</span>land<span class="token punctuation">)</span>
<span class="token keyword">const</span> <span class="token function-variable function">draw</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">data</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token comment">/* Retrieve the lowest and highest temperatures from our data */</span>
<span class="token keyword">const</span> domain <span class="token operator">=</span> d3<span class="token punctuation">.</span><span class="token function">extent</span><span class="token punctuation">(</span>data<span class="token punctuation">,</span> yAccessor<span class="token punctuation">)</span> <span class="token comment">// result: [-0.481, 1.018]</span>
<span class="token keyword">const</span> colorScale <span class="token operator">=</span> d3
<span class="token punctuation">.</span><span class="token function">scaleLinear</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">domain</span><span class="token punctuation">(</span>domain<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">range</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">'pink'</span><span class="token punctuation">,</span> <span class="token string">'blue'</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">clamp</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span></code></pre>
<p>The request response contains quite high resolution data. In the demo we’re using a function to downsample the data so that we are working with the average temperature anomaly on a yearly rather than monthly basis. This might not be necessary for your own dataset.</p>
<p>Let’s use our linear colour scale with our data to render a an SVG with a gradient fill that corresponds to temperature anomaly values over time — where higher temperatures are represented by colours towards the pink end of the scale and lower temperatures by bluer colours.</p>
<pre class="language-js"><code class="language-js"><span class="token comment">/* In our draw function */</span>
<span class="token keyword">const</span> colorScale <span class="token operator">=</span> d3
<span class="token punctuation">.</span><span class="token function">scaleLinear</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">domain</span><span class="token punctuation">(</span>domain<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">range</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">'pink'</span><span class="token punctuation">,</span> <span class="token string">'royalblue'</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">clamp</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span>
<span class="token comment">/* Create SVG */</span>
<span class="token keyword">const</span> svg <span class="token operator">=</span> chart
<span class="token punctuation">.</span><span class="token function">append</span><span class="token punctuation">(</span><span class="token string">'svg'</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">attr</span><span class="token punctuation">(</span><span class="token string">'width'</span><span class="token punctuation">,</span> <span class="token number">600</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">attr</span><span class="token punctuation">(</span><span class="token string">'height'</span><span class="token punctuation">,</span> <span class="token number">400</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">attr</span><span class="token punctuation">(</span><span class="token string">'viewBox'</span><span class="token punctuation">,</span> <span class="token string">'0 0 600 400'</span><span class="token punctuation">)</span>
<span class="token comment">/* Append <defs> element containing <linearGradient>, where we will define the gradient to use */</span>
<span class="token keyword">const</span> gradient <span class="token operator">=</span> svg<span class="token punctuation">.</span><span class="token function">append</span><span class="token punctuation">(</span><span class="token string">'defs'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">append</span><span class="token punctuation">(</span><span class="token string">'linearGradient'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">attr</span><span class="token punctuation">(</span><span class="token string">'id'</span><span class="token punctuation">,</span> <span class="token string">'grad1'</span><span class="token punctuation">)</span>
<span class="token comment">/* Create the gradient stops from our data */</span>
data<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">d<span class="token punctuation">,</span> index</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> color <span class="token operator">=</span> <span class="token function">colorScale</span><span class="token punctuation">(</span><span class="token function">yAccessor</span><span class="token punctuation">(</span>d<span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token keyword">const</span> step <span class="token operator">=</span> <span class="token number">100</span> <span class="token operator">/</span> data<span class="token punctuation">.</span>length
gradient<span class="token punctuation">.</span><span class="token function">append</span><span class="token punctuation">(</span><span class="token string">'stop'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">attr</span><span class="token punctuation">(</span><span class="token string">'offset'</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">attr</span><span class="token punctuation">(</span><span class="token string">'stop-colour'</span><span class="token punctuation">,</span> color<span class="token punctuation">)</span>
gradient
<span class="token punctuation">.</span><span class="token function">append</span><span class="token punctuation">(</span><span class="token string">'stop'</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">attr</span><span class="token punctuation">(</span><span class="token string">'offset'</span><span class="token punctuation">,</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>index <span class="token operator">*</span> step<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">%</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">attr</span><span class="token punctuation">(</span><span class="token string">'stop-colour'</span><span class="token punctuation">,</span> color<span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token comment">/* Create a rectangle and fill it with the gradient background */</span>
svg
<span class="token punctuation">.</span><span class="token function">append</span><span class="token punctuation">(</span><span class="token string">'rect'</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">attr</span><span class="token punctuation">(</span><span class="token string">'x'</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">attr</span><span class="token punctuation">(</span><span class="token string">'y'</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">attr</span><span class="token punctuation">(</span><span class="token string">'width'</span><span class="token punctuation">,</span> <span class="token number">600</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">attr</span><span class="token punctuation">(</span><span class="token string">'height'</span><span class="token punctuation">,</span> <span class="token number">400</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">attr</span><span class="token punctuation">(</span><span class="token string">'fill'</span><span class="token punctuation">,</span> <span class="token string">'url(#grad1)'</span><span class="token punctuation">)</span></code></pre>
<p>We <strong>could</strong> use a CSS gradient for this particular visualisation, but if we want to create a more complex chart that includes axes, labels, and more information, using an SVG is much more versatile.</p>
<figure>
<img src="https://css-irl.info/working-with-color-scales-02c.jpg" alt="Temperature chart with linear colour scale. Higher temperature anomalies are depicted by pink colours, lower anomalies by blue" width="1400" height="786" />
</figure>
<p>Note, when we use a linear scale both our domain and range can include a greater number of values, but it generally works best if both arrays are of the same length. In this example <code>colorScale(0)</code> will map to dark violet, <code>colorScale(100)</code> will map to red, and so on.</p>
<pre class="language-js"><code class="language-js">d3<span class="token punctuation">.</span><span class="token function">scaleLinear</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">domain</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token operator">-</span><span class="token number">100</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">range</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">'lightblue'</span><span class="token punctuation">,</span> <span class="token string">'darkviolet'</span><span class="token punctuation">,</span> <span class="token string">'red'</span><span class="token punctuation">]</span><span class="token punctuation">)</span></code></pre>
<h3>Diverging scales</h3>
<p>We can in fact use a different scale function for creating diverging colour scales: <code>scaleDiverging</code> always takes three values for its domain, similar to our linear scale function above. But instead of a range, we can specify an interpolator. D3 provides a bunch of colour scheme interpolators in its <a href="https://github.com/d3/d3-scale-chromatic">d3-scale-chromatic module</a>. The interpolator <code>d3.interpolateRdYlBu</code> is perfect for our temperature data, as it goes from red, through neutral colours, to dark blue. It also has a good degree of tonal contrast, with the lightness of the colours decreasing towards either end of the scale. This makes it more accessible for users that might find it difficult to perceive changes in hue.</p>
<p>We’ll flip the domain around in this instance, so that higher values will be represented by colours towards the red end of the scale and vice versa. We’re setting a middle value of 0, as 0 represents no temperature anomaly, which should be represented by a neutral colour.</p>
<p>Let’s apply this scale to our dataset.</p>
<pre class="language-js"><code class="language-js"><span class="token comment">/* Replace the previous `colorScale` value in our draw function: */</span>
<span class="token keyword">const</span> colorScale <span class="token operator">=</span> d3
<span class="token punctuation">.</span><span class="token function">scaleDiverging</span><span class="token punctuation">(</span>d3<span class="token punctuation">.</span>interpolateRdYlBu<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">domain</span><span class="token punctuation">(</span><span class="token punctuation">[</span>domain<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> domain<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">]</span><span class="token punctuation">)</span></code></pre>
<p class="codepen" data-height="481" data-default-tab="result" data-slug-hash="QWVWdjz" data-user="michellebarker" style="height: 481px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/QWVWdjz">
D3 colour scale linear (diverging)</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<h2>Interpolation</h2>
<p>As we have seen, D3’s scale functions interpolate between two (or more) colours to produce hues that fall anywhere between those in our specified colour range. We can choose how the colours are interpolated on a linear scale using D3’s various interpolation methods. Specifying <code>interpolateCubeHelix</code> as an interpolation method, for example, will yield quite a different result to <code>interpolateRGB</code>.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> colorScale <span class="token operator">=</span> d3
<span class="token punctuation">.</span><span class="token function">scaleLinear</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">domain</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">100</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">range</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">'royalblue'</span><span class="token punctuation">,</span> <span class="token string">'pink'</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">interpolate</span><span class="token punctuation">(</span>d3<span class="token punctuation">.</span>interpolateCubehelix<span class="token punctuation">)</span></code></pre>
<p>colour interpolation is a huge topic, beyond the scope of this article. Check out the documentation on colour spaces in the <a href="https://github.com/d3/d3-interpolate#colour-spaces">d3-interpolate</a> module for details of the various interpolation methods available in D3 — and try playing around with them to see the different results.</p>
<h2>Discrete scales</h2>
<p>We’re going to take a look at two of D3’s scale functions for working with <a href="https://www.g2.com/articles/discrete-vs-continuous-data">discrete data</a> — data that consists of individual or countable values. Using our temperature data, perhaps we want to draw a line graph showing distinct lines for temperature over time during the spring, summer, autumn and winter respectively. Spring, summer, autumn and winter are discrete values (they are each distinct), while temperature is continuous.</p>
<h3>Ordinal scale</h3>
<p>If we want to colour our lines distinctly for the four seasons we could use an ordinal scale. This maps a discrete input to a discrete output with a 1:1 relationship.</p>
<pre class="language-js"><code class="language-js">d3<span class="token punctuation">.</span><span class="token function">scaleOrdinal</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">domain</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">'spring'</span><span class="token punctuation">,</span> <span class="token string">'summer'</span><span class="token punctuation">,</span> <span class="token string">'autumn'</span><span class="token punctuation">,</span> <span class="token string">'winter'</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">range</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">'turquoise'</span><span class="token punctuation">,</span> <span class="token string">'pink'</span><span class="token punctuation">,</span> <span class="token string">'orangered'</span><span class="token punctuation">,</span> <span class="token string">'royalblue'</span><span class="token punctuation">]</span><span class="token punctuation">)</span></code></pre>
<p class="codepen" data-height="481" data-default-tab="result" data-slug-hash="YzjMMNp" data-user="michellebarker" style="height: 481px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/YzjMMNp">
D3 ordinal colour scale</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>We would expect our data to fall into any one of these categories. On the other hand, if we were displaying a bar chart showing the number of survey respondents categorized by gender, there could be some responses that do not conform to our three categories: a respondent might have entered a different gender in their response, or declined to answer. We can use the unknown() method to define a colour for unexpected or undefined values:</p>
<pre class="language-js"><code class="language-js">d3<span class="token punctuation">.</span><span class="token function">scaleOrdinal</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">domain</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">'male'</span><span class="token punctuation">,</span> <span class="token string">'female'</span><span class="token punctuation">,</span> <span class="token string">'non-binary'</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">range</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">'blue'</span><span class="token punctuation">,</span> <span class="token string">'cornflowerblue'</span><span class="token punctuation">,</span> <span class="token string">'lightblue'</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">unknown</span><span class="token punctuation">(</span><span class="token string">'grey'</span><span class="token punctuation">)</span></code></pre>
<h3>Threshold scale</h3>
<p>A threshold scale is useful when we have continuous numerical input that we want to map to a discrete output. In our temperature data, it could be difficult to determine small changes in temperature when using a continuous colour scale. But using a threshold scale, we can show when the temperature crosses a given threshold, and provide visually distinct colours (see <a href="https://codepen.io/michellebarker/pen/NWBZQRj">the demo</a>). The scale’s domain should consist of the threshold boundaries.</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> colorScaleThreshold <span class="token operator">=</span> d3
<span class="token punctuation">.</span><span class="token function">scaleThreshold</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">domain</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token operator">-</span><span class="token number">0.25</span><span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> <span class="token number">0.25</span><span class="token punctuation">,</span> <span class="token number">0.5</span><span class="token punctuation">,</span> <span class="token number">0.75</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">range</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">'royalblue'</span><span class="token punctuation">,</span> <span class="token string">'lightblue'</span><span class="token punctuation">,</span> <span class="token string">'azure'</span><span class="token punctuation">,</span> <span class="token string">'pink'</span><span class="token punctuation">,</span> <span class="token string">'hotpink'</span><span class="token punctuation">]</span><span class="token punctuation">)</span></code></pre>
<p>If we use this scale for our temperature data we can clearly see the marked increase in temperature anomalies in the early part of this century. For finer detail, we could increase the number of colour thresholds.</p>
<figure>
<img src="https://css-irl.info/working-with-color-scales-02b.jpg" alt="Temperature chart with threshold scale applied, showing distinct colour bands" width="1200" height="578" />
</figure>
<p>Rather than manually set our threshold colours, we could compute these from our linear or diverging scales.</p>
<pre class="language-js"><code class="language-js"><span class="token comment">/* In our draw function: */</span>
<span class="token comment">/* Create the continuous colour scale to interpolate threshold colour values */</span>
<span class="token keyword">const</span> colorScaleDiverging <span class="token operator">=</span> d3
<span class="token punctuation">.</span><span class="token function">scaleDiverging</span><span class="token punctuation">(</span>d3<span class="token punctuation">.</span>interpolateRdYlBu<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">domain</span><span class="token punctuation">(</span>linearDomain<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">clamp</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span>
<span class="token comment">/* Create the threshold scale domain - equally spaced values from the data extent */</span>
<span class="token keyword">const</span> totalThresholds <span class="token operator">=</span> <span class="token number">8</span>
<span class="token comment">// Calculate the value between each threshold</span>
<span class="token keyword">const</span> step <span class="token operator">=</span> <span class="token punctuation">(</span>domain<span class="token punctuation">[</span><span class="token number">1</span><span class="token punctuation">]</span> <span class="token operator">-</span> domain<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">/</span> totalThresholds
<span class="token keyword">const</span> thresholdScaleDomain <span class="token operator">=</span> d3<span class="token punctuation">.</span><span class="token function">range</span><span class="token punctuation">(</span>totalThresholds<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">d</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token punctuation">(</span>d <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span> <span class="token operator">*</span> step <span class="token operator">+</span> domain<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token comment">/* Create the threshold scale range using our previous colour scale function. It sould have the same number of values as the domain */</span>
<span class="token keyword">let</span> thresholdScaleRange <span class="token operator">=</span> thresholdScaleDomain<span class="token punctuation">.</span><span class="token function">map</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">d</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> <span class="token function">colorScaleDiverging</span><span class="token punctuation">(</span>d<span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
thresholdScaleRange <span class="token operator">=</span> <span class="token punctuation">[</span>
<span class="token function">colorScaleLinear</span><span class="token punctuation">(</span>domain<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token comment">// include the lowest threshold, so we don't miss out on the first colour</span>
<span class="token operator">...</span>thresholdScaleRange<span class="token punctuation">,</span>
<span class="token punctuation">]</span>
<span class="token comment">/* Use these in our new colour scale function */</span>
<span class="token keyword">const</span> colorScaleThreshold <span class="token operator">=</span> d3
<span class="token punctuation">.</span><span class="token function">scaleThreshold</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">domain</span><span class="token punctuation">(</span>thresholdScaleDomain<span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">range</span><span class="token punctuation">(</span>thresholdScaleRange<span class="token punctuation">)</span></code></pre>
<p>Using a threshold scale rather than a linear scale, we can more clearly see the temperature contrast over the long term with getting too granular. Reducing the number of thresholds creates a starker visual disparity between the beginning of the last century and recent years, as we can clearly see the proportion of data that falls above given thresholds. Try adjusting the number of thresholds in this demo to see the impact.</p>
<p class="codepen" data-height="489" data-default-tab="result" data-slug-hash="oNPNYmL" data-user="michellebarker" style="height: 489px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/oNPNYmL">
D3 colour scale threshold (diverging)</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>Hopefully this article has given you a good overview of where different types of scales might come useful when working with colour and data visualisation.</p>
Testing Colour Accessibility with Dev Tools2023-02-07T00:00:00Zhttps://css-irl.info/testing-colour-accessibility-with-dev-tools/<p>I often use the <a href="https://webaim.org/resources/contrastchecker/">WebAIM colour contrast checker</a> to check my colours against WCAG (Web Content Accessibility Guidelines) standards while building websites. Recently I noticed a handy feature in Firefox dev tools to make checking for sufficient colour contrast really easy.</p>
<figure>
<img src="https://css-irl.info/testing-color-accessibility-with-dev-tools-01.jpg" width="465" height="361" alt="Screenshot of the Firefox colour picker with accessibility informations" />
</figure>
<p>If you inspect an element’s CSS in the dev tools inspector you’ll notice any colours are accompanied by a little colour swatch. Clicking on this brings up a colour picker — handy for trying out different colour options in the browser. For the <code>color</code> property, this colour picker also shows the contrast ratio of the text colour compared to the colour beneath (either the element’s background colour, or the background colour of an element beneath). It turns out Chrome has a very similar feature too. That’s super useful for experimenting with accessible colour combinations, without even leaving your browser tab!</p>
<h2>Colour contrast simulator</h2>
<p>Firefox has another useful feature for checking our colours are accessible: the colour contrast simulator. Found in the “Accessibility” tab in the dev tools panel, this feature simulates different colour vision deficiencies. <a href="https://firefox-source-docs.mozilla.org/devtools-user/accessibility_inspector/simulation/index.html">Check out the full write-up</a>.</p>
Interop 20232023-02-02T00:00:00Zhttps://css-irl.info/interop-2023/<p>Last year, <a href="https://wpt.fyi/interop-2022">Interop 2022</a>, a collaborative cross-browser initiative, was hugely successful in getting 15 key features implemented interoperably. As developers, we can enjoy the fruits of this collaboration, with features like <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Container_Queries">container queries</a>, <a href="https://css-tricks.com/css-cascade-layers/">cascade layers</a> and <a href="https://web.dev/viewport-units/">dynamic viewport units</a> going from little or no browser support to almost full support in the space of a year.</p>
<p><a href="https://wpt.fyi/interop-2023">Interop 2023</a> has just been announced, expanding to 26 focus areas. Some exciting CSS features include the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:has"><code>:has()</code> pseudo-class</a>, which is already supported in some browsers, as well as <a href="https://drafts.csswg.org/mediaqueries-4/">Level 4 media queries</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Functions#math_functions">math functions</a>, and <a href="https://webkit.org/blog/12662/customizing-color-fonts-on-the-web/">font palette features</a>, which enable custom coloured fonts.</p>
<p>Read the Interop teams’ write-ups here:</p>
<ul>
<li><a href="https://webkit.org/blog/13706/interop-2023">WebKit</a></li>
<li><a href="https://web.dev/interop-2023/">Google</a></li>
<li><a href="https://www.igalia.com/news/2023/interop2023.html">Igalia</a></li>
<li><a href="https://blogs.windows.com/msedgedev/2023/02/01/microsoft-edge-and-interop-2023/">Microsoft</a></li>
<li><a href="https://hacks.mozilla.org/2023/02/announcing-interop-2023/">Mozilla</a></li>
<li><a href="https://bocoup.com/blog/interop-2023">Bocoup</a></li>
</ul>
Scheduling Netlify Deployments with Github Actions2023-01-31T00:00:00Zhttps://css-irl.info/scheduling-netlify-deployments-with-github-actions/<p>In this article we’ll walk through how to use Github Actions to automate regular deployment of a static site hosted on <a href="https://www.netlify.com/">Netlify</a>.</p>
<h2>Why</h2>
<p>This site (the one you’re reading right now) is a static site built with <a href="https://www.11ty.dev/">Eleventy</a>. That means there’s no server building the pages on demand. Instead, every time I write a blog post in markdown and commit the file to the <a href="https://github.com/mbarker84/css-irl-eleventy">Github repository</a>, the site is re-built — with Eleventy generating a static HTML page from my markdown file — and deployed on Netlify’s servers. Pretty cool, right?</p>
<p>In order to set up automated deployments you need to link your Github repository to your Netlify account, then configure your build settings — including the build command you want to use (e.g. <code>npm run build</code>), and the directory to publish from. This isn’t a tutorial on how to deploy a site to Netlify, but <a href="https://docs.netlify.com/">the docs</a> have you covered if you’re new to it.</p>
<h3>Webmentions</h3>
<p>As things stand, the site will deploy every time I push to the <code>main</code> branch. Now, I recently implemented <a href="https://indieweb.org/webmention.io">Webmentions</a> on the site, so users can see if anyone has commented or shared the posts on Mastodon or Twitter. I thought it would be nice to do this without any client-side Javascript, by fetching all the webmentions at build time. <a href="https://sia.codes/">Sia</a> has a great in-depth article on <a href="https://sia.codes/posts/webmentions-eleventy-in-depth/">implementing webmentions with Eleventy and Netlify</a> if you’re interested in trying it yourself. Admittedly, I cheated somewhat and used the <a href="https://github.com/CodeFoodPixels/eleventy-plugin-webmentions">Eleventy webmentions plugin</a>, which does a similar thing. Sia’s article is well worth a read regardless, as there are some handy tips in there, like creating custom filters for your templates.</p>
<p>So far so good, but what if I don’t update my website for a few days or longer (it’s been known to happen!)? I’d still quite like my mentions to be up-to-date. And here we finally come to the point of this article: we can use a Github action to trigger regular builds, which will cause new webmentions to be fetched, and the site to be deployed to Netlify.</p>
<h2>How</h2>
<p>If you don’t yet have a Netlify account, I recommend reading the docs to sign up, add your first site by linking a Github repository, and configure your build settings.</p>
<h3>Create a build hook</h3>
<p>Once we have all that, we need to create a <a href="https://docs.netlify.com/configure-builds/build-hooks/">build hook</a> in Netlify. You can add a build hook in the ‘Build and deploy’ section of your site settings, giving it a name. I’m using <code>NETLIFY_DAILY_BUILD</code> in this case.</p>
<figure>
<img src="https://css-irl.info/scheduling-netlify-deployments-with-github-actions-01.jpg" alt="Netlify UI showing the location of the Build and Deploy section on the left" width="1366" height="750" />
</figure>
<p>When you’ve created the build hook you’ll be given an API endpoint, including a unique token, which we’ll use in our Github action in a moment.</p>
<h3>Create a Github secret token</h3>
<p>Let’s hop on over to the Github repository. It’s good practice to store our token securely if our repository is public. We can store this in Github by creating a new secret for the repository with our Netlify build hook token as its value.</p>
<figure>
<img src="https://css-irl.info/scheduling-netlify-deployments-with-github-actions-03.jpg" alt="Github UI showing where to create the secret token" width="1366" height="750" />
</figure>
<h3>Set up a Github action</h3>
<p>To set up a Github action, go to the “Actions” tab in your Github repository, and select “New workflow”. Github gives you a bunch of workflows to choose from, but we’ll need to create our own, so go to “set up a workflow yourself”.</p>
<figure>
<img src="https://css-irl.info/scheduling-netlify-deployments-with-github-actions-02.jpg" alt="Github UI showing where to set up a new action" width="1366" height="750" />
</figure>
<p>This opens up a new Yaml file, where we’ll write the code for our action. Alternatively, you could create the file in the root of your project and commit it.</p>
<p>We can add the following code in order to trigger our daily build. You can customise the action name and step name(s) as desired.</p>
<pre class="language-yml"><code class="language-yml"><span class="token key atrule">name</span><span class="token punctuation">:</span> Scheduled build
<span class="token key atrule">on</span><span class="token punctuation">:</span>
<span class="token key atrule">schedule</span><span class="token punctuation">:</span>
<span class="token punctuation">-</span> <span class="token key atrule">cron</span><span class="token punctuation">:</span> <span class="token string">'0 9 * * *'</span>
<span class="token key atrule">jobs</span><span class="token punctuation">:</span>
<span class="token key atrule">build</span><span class="token punctuation">:</span>
<span class="token key atrule">runs-on</span><span class="token punctuation">:</span> ubuntu<span class="token punctuation">-</span>latest
<span class="token key atrule">steps</span><span class="token punctuation">:</span>
<span class="token punctuation">-</span> <span class="token key atrule">name</span><span class="token punctuation">:</span> Trigger our build webhook on Netlify
<span class="token key atrule">env</span><span class="token punctuation">:</span>
<span class="token key atrule">TOKEN</span><span class="token punctuation">:</span> $<span class="token punctuation">{</span><span class="token punctuation">{</span> secrets.NETLIFY_DAILY_BUILD <span class="token punctuation">}</span><span class="token punctuation">}</span>
<span class="token key atrule">run</span><span class="token punctuation">:</span> curl <span class="token punctuation">-</span>X POST <span class="token punctuation">-</span>d <span class="token punctuation">{</span><span class="token punctuation">}</span> https<span class="token punctuation">:</span>//api.netlify.com/build_hooks/$<span class="token punctuation">{</span>TOKEN<span class="token punctuation">}</span></code></pre>
<p>The “schedule” option determines how often we want the build hook to run. <code>0 9 * * *</code> dictates that the build should run at 9am every day. (The order is: minutes, hours, days, weeks, months.) If we want it to run more frequently than that, we could write <code>0 */6 * * *</code> say, to run it every four hours. <a href="https://crontab.guru/">Crontab Guru</a> can help you create custom expressions if you need something more complex.</p>
<p>The last step in the code is running the build hook by sending a <code>POST</code> request to the URL. We’re using the token from the secret we just created instead of copying the Netlify URL in its entirety, to prevent anyone accessing the URL and spamming it (which would eat up all our Netlify build allowance!). Don’t forget to substitute your own variable if you’ve called it something else.</p>
<p>Once you save the file and commit in to your repository, the daily build should run at the scheduled time.</p>
<h2>Further reading</h2>
<p>All this puts me in mind of a project by <a href="https://www.hawksworx.com/">Phil Hawksworth</a> a few years ago. Phil built a <a href="https://www.netlify.com/blog/2018/08/02/exploring-the-potential-of-friction-free-deployments/?utm_campaign=devex_pnh&utm_source=devto&utm_medium=web&utm_content=night-night-setyourwatch">silly-but-fun site</a> that would display the current time to the user without any client-side Javascript, by deploying to Netlify every minute.</p>
<h3>Edge Functions</h3>
<p>More recently, Phil updated this to use <a href="https://www.netlify.com/products/#netlify-edge-functions">Edge Functions</a>, a feature currently in beta on Netlify. (<a href="https://dev.to/philhawksworth/what-i-learned-from-automating-millions-of-web-site-deploys-3akg">Read about it in this article</a>.)</p>
<p>Edge Functions run on-demand, so we could run a function when the user visits the site and still deliver a pre-rendered page. It got me wondering whether we could fetch webmentions using an Edge Function, thereby delivering the very latest mentions pre-rendered to the user, without having to wait for a scheduled build. I have no idea whether this is sensible or not, but perhaps something to explore!</p>
A Quick Website Redesign2023-01-25T00:00:00Zhttps://css-irl.info/a-quick-website-redesign/<p>In my <a href="https://css-irl.info/2022-in-review/">2023 review</a> I mentioned that I wanted to give this site a revamp. This week I carved out some time to do just that. So, here it is: Welcome to the new and improved CSS { IRL }!</p>
<h2>Design improvements</h2>
<p>I felt like the previous UI was becoming a bit muddled as I added new features on an ad-hoc basis, without sitting down and committing to a plan. Also, I was just sick of looking at it! So this design is a bit more stripped back visually, without too many unnecessary flourishes, while still keeping some nice little touches, like <a href="https://css-irl.info/animating-underlines/">animated underlines</a> on hover. Other improvements include a better mobile experience (via the expanding/collapsing tag menu), and the ability to filter by category of post (currently “articles”, “notes” or “quick tips”, which I might add to over time).</p>
<p>If I had any sense I’d do a few visual mockups in Figma or something before jumping straight into the code. But I’m an impatient developer, at least when it comes to my personal projects, and this is probably a masterclass in how <strong>not</strong> to do a redesign. All the planning went on very much in my head, supplemented by short, handwritten to-do list. Analogue, baby!</p>
<p>But regardless, I’m pretty happy with the result. The layout has less visual clutter, and a lot more breathing space. Looking at it makes me feel calm. Hopefully I can resist the temptation to add complexity over time.</p>
<h2>The technical stuff</h2>
<p>There has been quite a bit of refactoring behind the scenes, but still plenty more to do. I’ve cleaned up and stripped out a lot of old CSS, and leaned even more into custom properties. But I’ve also been lazy with some of my naming conventions and CSS architecture (sshhh, don’t tell anyone 🤫), in the interest of avoiding delaying deployment. I’ve learnt from experience that waiting for perfection means waiting a looooong time, and have long been fighting my perfectionist urges in order to put <strong>something</strong> out into the world!</p>
<p>I’m still considering rebuilding with <a href="https://astro.build/">Astro</a>, but that’s a bigger job, plus the current tooling works just fine for now. I’m content to just deploy the redesign for now, and at least tick one thing off my to-do list!</p>
<h2>Future feature inspiration</h2>
<p>During the redesign process I started thinking about some of the things I’d like to add to the site over time. As I mentioned in a previous post, the recent Twitter debacle has underlined the importance (to me, at least) of having somewhere you can call “home” on the web, where <strong>you</strong> own your content, not some tech wanker.</p>
<h3>Personalisation</h3>
<p>I love the idea adding more personalisation, and have been enjoying these articles by <a href="https://localghost.dev//blog/everything-should-have-an-api-adventures-in-trying-to-automate-stuff/">Sophie Koonin</a> and <a href="https://rknight.me/automating-my-now-page/">Robb Knight</a> on automating a “now” page. Perhaps I’ll get around to doing that sometime.</p>
<p>I’m also inspired by <a href="https://ohhelloana.blog/">Ana Rodrigues’s blog</a>, where nearly post has a “now” section. In fact, Ana’s whole blog feels incredibly intimate and personal, even down to her writing style, which I love. The challenge will be incorporating personal touches without adding the aforementioned clutter.</p>
<h3>Webmentions</h3>
<p><a href="https://andy-bell.co.uk/">Andy Bell</a> recently wrote about how he implemented an <a href="https://andy-bell.co.uk/improving-likes-on-my-site/">expandable webmentions</a> section on his site, which is super cool — and also reminded me that I should get webmentions on here. If you’re not too familiar with webmentions, <a href="https://daily-dev-tips.com/posts/goodbye-comments-welcome-webmentions/">this article I came across</a> seems like a good explainer.</p>
<h3>Projects</h3>
<p>One of the next features I’d like to add is a “projects” section, to share some of my work that isn’t exclusively blog posts. Look out for that soon perhaps. In the meantime, enjoy the new site!</p>
A Useful VS Code Extension for Environment Switching2023-01-18T00:00:00Zhttps://css-irl.info/a-useful-vscode-extension-for-environment-switching/<p>Do you find yourself needing to switch between development environments frequently? That's often the case for me. I work on a web app for wind turbine operators, and I regularly need to test my front end code with different databases.</p>
<p>My colleague recently alerted me to <a href="https://marketplace.visualstudio.com/items?itemName=EcksDy.env-switcher">.ENV Switcher</a>, a VS Code extension that makes switching between different environments simple. Once installed, you need to create your different .env files (such as dev.env, release.env). Then you bring up a list of .env files to select from by clicking on a small button at the bottom of the window, and instantly switch to the desired environment. Nice!</p>
<p><a href="https://marketplace.visualstudio.com/items?itemName=EcksDy.env-switcher">Get the extension →</a></p>
Disentangling Frameworks2023-01-17T00:00:00Zhttps://css-irl.info/disentangling-frameworks/<p>The other day I came across a pretty much textbook use case for container queries in the web app I was working on. We have this card component that displays a health summary for the section of the app the user is currently inspecting (which could be a wind turbine, a system within that turbine, or the entire wind farm). Generally we want to show that towards the top left of the screen, with some other information on the right. On certain pages however, the information on the right will be absent, so we want the health summary component to go full width. Easy enough, but we also want to style the component differently when it takes up the entire width by aligning the text to the left and giving it a more horizontal layout.</p>
<figure>
<img src="https://css-irl.info/disentangling-frameworks-01.svg" alt="UI layout with two widgets side-by-side" />
<figcaption>When we have two widgets to display, the left one should have centered text</figcaption>
</figure>
<figure>
<img src="https://css-irl.info/disentangling-frameworks-02.svg" alt="UI layout with one wide widget, with a horizontal layout" />
<figcaption>When the right-hand widget is absent, we should left-align the text of the other one, and display is as two columns</figcaption>
</figure>
<p>“Hooray!” I thought. I can use a <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Container_Queries">container query</a>, and include the <a href="https://github.com/GoogleChromeLabs/container-query-polyfill">polyfill</a> to cover the ever-shrinking number of browsers that don’t yet support container queries. (Firefox is the only evergreen browser that doesn’t support them, at the time of writing). Here’s how we’d write those styles with a container query:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.container</span> <span class="token punctuation">{</span>
<span class="token property">container</span><span class="token punctuation">:</span> widget / inline-size<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token atrule"><span class="token rule">@container</span> widget <span class="token punctuation">(</span>inline-size > 500px<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token selector">.component</span> <span class="token punctuation">{</span>
<span class="token comment">/* Styles to be rendered when the component is wider than 500px */</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>There’s just one problem: The codebase uses <a href="https://tailwindcss.com/">Tailwind</a>, a utility class CSS framework. To style an element with Tailwind, we append multiple classes (or “utilities”) to an element to handle individual CSS properties, rather than styling the element in our CSS file.</p>
<p>With Tailwind:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text-5xl text-blue-700 p-4 mb-8<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Hello world<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></code></pre>
<p>As opposed to styling in regular CSS:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">h1</span> <span class="token punctuation">{</span>
<span class="token property">font-size</span><span class="token punctuation">:</span> 3rem<span class="token punctuation">;</span>
<span class="token property">line-height</span><span class="token punctuation">:</span> 1<span class="token punctuation">;</span>
<span class="token property">color</span><span class="token punctuation">:</span> <span class="token function">rgb</span><span class="token punctuation">(</span>29 78 216<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">padding</span><span class="token punctuation">:</span> 1rem<span class="token punctuation">;</span>
<span class="token property">margin-bottom</span><span class="token punctuation">:</span> 2rem<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>As frameworks go, Tailwind is perhaps the most divisive, with a lot of strong opinions batted back and forth on both sides. This isn’t a post arguing for or against Tailwind, so I’ll save my opinions for another time. But on the whole I take a pragmatic view: while I don’t elect to use it on my own personal projects, in my experience it has its advantages when working in a team.</p>
<p>What I like about though, it that in theory you can use as much or as little Tailwind as you need. Like, if you want to just use it to generate a bunch of utility classes for commonly used colours in your app you can. Everything else should just get stripped out at compile time, and you can happily write the rest of your CSS in a regular old CSS file.</p>
<h2>Yes, but...</h2>
<p>So, why is this a problem when using container queries? Well, the polyfill doesn’t seem to work with a Tailwind project. This could be a a unique issue to do with my bundler as well as Tailwind itself in conjunction with the polyfill. I’m sure given the time I could more-or-less figure out <strong>why</strong> it doesn’t work — the polyfill is loading and doing <strong>something</strong>, it’s just not having any effect on the container-specific styles. If I remove Tailwind, the polyfill works as expected. In this case, the simplest solution, given the time constraints, was to scrap the container query and implement the desired styles a different way instead.</p>
<aside><p>Tailwind has an official container query plugin for writing container queries as utility classes. When I tried this approach I had the same issue with the polyfill not working. However, my preference would be to write container queries in actual CSS!</p></aside>
<h2>Un-Tailwinding</h2>
<p>But really that’s neither here nor there. The point is that it made me pause and think about the repercussions of introducing dependencies into our projects. Suppose we want to stop using Tailwind one day?</p>
<p>Tailwind has some pretty strong opinions on how it wants you to write CSS. The documentation encourages developers, even when breaking out into a separate CSS file (something that the authors try to dissuade you from doing unless absolutely necessary), to use their <code>@apply</code> directive, meaning that all of your CSS is dependent on being compiled with Tailwind.</p>
<p>So if we decided we didn’t want to use Tailwind anymore, it would be pretty hard to extract and compile our Tailwind CSS into a manageable CSS file. Interestingly, someone has built a <a href="https://tailwind-to-css.vercel.app/">tool to extract classes into regular CSS</a>. But a large project would require <strong>a lot</strong> of manual copying and pasting, and refactoring, and it would be easy to mess up. My hunch is that the best option would be to refactor individual components gradually over time, until you have just regular CSS and can remove the dependency entirely.</p>
<h2>The platform always wins</h2>
<p>My point is that when evaluating the use of a dependency that has such a large reach within a project, it’s wise to also consider what happens when that dependency is no longer useful to us, or when a new release contains significant breaking changes that it might necessitate a large refactor to keep our project up to date. This is true of JS dependencies as well as CSS frameworks.</p>
<p>Sometimes we can’t avoid a large dependency: many projects built with React or Vue or similar frameworks would be unfathomably complicated to manage without. But these frameworks have relatively quick release cycles, and require maintenance to keep up to date.</p>
<p>By comparison, web standards evolve relatively slowly. But they evolve slowly for a reason: features that are added now need to be supported <strong>forever</strong>. They are not designed to become obsolete. So whenever possible, the safest, and most future-proof bet is to use the native features of the web platform.</p>
2022 in Review2023-01-02T00:00:00Zhttps://css-irl.info/2022-in-review/<p>As is tradition, it’s time to do a little round-up of the past year!</p>
<h2>Work</h2>
<p>At the end of 2021 I started a new job as Senior Front End Developer at <a href="https://www.ada-mode.com/">Ada Mode</a>, a data science and AI company working in the renewable energy sector. It’s been a great year for the company, as we’ve taken on a bunch of exciting projects and continued to make a name for ourselves. The year has been full of interesting challenges and learning opportunities for me personally, working with Vue and <a href="https://d3js.org/">D3.js</a> in particular, (and AWS, which has been less fun 😅) and it’s been a pleasure to work with the friendly and knowledgeable team. I’m looking forward to the company continuing to grow in 2023 as we hire new developers and begin projects spanning sustainable agriculture, wind energy, nuclear decommissioning, solar power and more.</p>
<h2>Writing</h2>
<p>I’ve continued to dedicate one day a week to writing, speaking and personal projects in 2022. This includes writing a total of seven articles for other publications (<a href="https://www.smashingmagazine.com/">Smashing Magazine</a>, <a href="https://tympanus.net/codrops/">Codrops</a> and Google’s <a href="https://web.dev/">Web.dev</a>), as well as averaging two articles per month on this blog. One of my most popular articles this year was <a href="https://css-irl.info/aspect-ratio-is-great/">Aspect Ratio is Great</a>, all about the CSS <code>aspect-ratio</code> property. It always surprises me that what I consider the simplest articles often end up being the most popular! <code>aspect-ratio</code> already feels like old news now, an indication of how far CSS layout has come in 2022 (more on that later).</p>
<p>I don’t feel I’m writing quite enough for clients compared to what I aimed for when I reduced my working hours at my “main” job in 2021. Back then I optimistically thought I might write a paid article per week. It definitely hasn’t worked out that way, as in reality the writing process takes longer when I’m writing for someone else. Also a lot of that writing time has been taken up with other important things, such as conference talk prep, unpaid work (like this blog) and childcare. On balance, I’m okay with that for now.</p>
<p>This year I’ve begun to write more about climate action in tech. The climate crisis is something we cannot afford to ignore, whatever industry we work in, and the way I see it, those of us working in the tech industry have more power than most to do something about it and be an influence for good. While some might see it as a departure from the original focus of this blog, I believe it is intrinsically linked, and there is plenty that front end developers can do to minimise our environmental impact and be a force for good. So don’t expect me to dial down on it in 2023.</p>
<h3>This blog</h3>
<p>While I don’t have any major plans to change the direction of writing on this blog, my fingers have been itching for a redesign for a little while. I’d like to implement an improved system of categorisation, to help with publishing quick notes as well as longer articles. This is partly spurred on by the collapse of Twitter, where I would normally share quick thoughts and tips. Like many others, I’m realising the benefit of owning your own content, on your own corner of the web.</p>
<p>I’m also considering using <a href="https://astro.build/">Astro</a> to build the next iteration of this site. I’ve managed to find time to play around with it a little bit in 2022, and I’m seriously impressed. As much as I enjoy using <a href="https://www.11ty.dev/">Eleventy</a>, Astro lets you build a blog with absolutely minimal configuration, integrates well with a whole host of different tools, and the docs are great too.</p>
<h2>Speaking</h2>
<p>I spoke at three in-person events and one online event in 2022, which is pretty much the most I’d want to do in any given year. The highlight was speaking at <a href="https://cssday.nl/2022">CSS Day</a> in Amsterdam, where I got to meet and speak alongside some incredibly talented people (and personal heroes). It was probably the most nervous I’ve ever felt speaking at a conference, but the organisers, speakers and attendees were all wonderful and supportive, and it was an incredible experience.</p>
<p>Later in the year I had the privilege of travelling to Freiburg to speak at my first in-person <a href="https://smashingconf.com/freiburg-2022">Smashing Conference</a>. Despite a fairly stressful journey (my train got stuck for 8 hours!), it was a pleasure to visit the city and be part of the conference, run by a lovely team. I would love to spend more time in Freiburg on another occasion.</p>
<p>I mostly spoke about CSS layout this year, but in early 2022 I was invited to speak at <a href="https://www.meetup.com/toronto-web-performance-group/events/283332963/">Toronto Web Performance Meetup</a> (online) for their special edition focusing on low-carbon web development. This was my first time speaking about something other than CSS, and I got to speak alongside <a href="https://fershad.com/">Fershad Irani</a> and <a href="https://twitter.com/jonarnes">Jon Arne S</a>, who are both active members of the climate action tech community. Although I felt like a novice in comparison, I had to remind myself that speaking about a topic as someone relatively new to the subject is just as valid, and can bring a different perspective compared to those with more experience. (If you ever feel like you’re not qualified to talk about something, remind yourself of this!).</p>
<p>I already have a couple of speaking gigs lined up for 2023, including <a href="https://beyondtellerrand.com/events/dusseldorf-2023">Beyond Tellerand</a>, which I’m super excited about.</p>
<h2>CSS</h2>
<p>2022 was a bumper year for CSS! I’ve been mostly focused on CSS layout, where we’ve seen long-awaited features like <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Container_Queries">container queries</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Container_Queries#container_query_length_units">container units</a>, both game-changers for CSS. The <code>:has()</code> pseudo-class is another exciting feature with huge potential to change how we write CSS. <a href="https://developer.mozilla.org/en-US/docs/Learn/CSS/Building_blocks/Cascade_layers">Cascade layers</a> are something I started 2022 without giving much thought to, but have ended up feeling very enthusiastic about.</p>
<p>It surprised me that Chrome has finished the year way behind on its <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout/Subgrid">subgrid</a> implementation compared to other browsers, but hopefully we’ll see more widespread support in 2023.</p>
<p>It’s great to see browsers working together to implement CSS features interoperably, with the launch of <a href="https://wpt.fyi/interop-2022">Interop 2022</a>. Although 2022 has been an exceptional year for CSS features, hopefully we’ll see this kind of collaboration continue.</p>
<h2>Personal goals and achievements</h2>
<p>I ended last year with the goal of finding time to be more playful and creative with CSS again. This hasn’t exactly worked out consistently, but there have been opportunities here and there to play around with new CSS features and make demos while working on talks and articles. In December I also built a little <a href="https://codepen.io/michellebarker/pen/WNKbQOO">tool for making CSS pixel art</a>, which is one of my more creative projects this year. It’s nice to end the year on a high with that.</p>
<p>One reason for having less time for creative CSS projects was due to pivoting my focus somewhat. Through the <a href="https://climateaction.tech/">ClimateAction.Tech</a> community I’ve become involved with voluntary work around climate action and web sustainability. I’m part of the <a href="https://www.w3.org/community/sustyweb/">W3C Sustainable Web Design Community Group</a>, where we are aiming to produce a collection of guidelines and resources to aid developers in building sustainable websites. I also started doing some voluntary web development work for a climate activist organisation. It’s been a nice way to keep my hand in building traditional websites, as an alternative to the web apps I mainly build for work.</p>
<p>I’m pleased that I was successful in my goal of maintaining a healthy work-life balance, and saying “no” to the things I don’t have the energy for, even when it meant making some tough choices. After working hard for years (including spending a lot of my free time honing my skills), this year I felt like I could finally take my foot off the gas, look around and appreciate where my hard work has got me. It’s the first time I haven’t had a clear idea of what the next career goal is, and I think that’s okay. The most important thing for me is continue to do work that is meaningful and has a positive impact, spend time with my family, and have time for my hobbies away from the screen. I plan to do more drumming, reading and exercise in 2023!</p>
<p>Happy New Year to you and your loved ones, wherever you are.</p>
Correcting With Kindness2022-12-27T00:00:00Zhttps://css-irl.info/correcting-with-kindness/<p>Quite a lot of us tech writers don’t get everything 100% right all of the time. Sometimes a small typo might elude us, sometimes we make the odd factual error despite our best efforts and research. Sometimes we phrase something clumsily, giving the wrong impression, omit some piece of information (intentionally or unintentionally), or perhaps misinterpret something we’ve read elsewhere. I’m as guilty as anyone. Especially on this blog, where I don’t always have time to proofread as thoroughly as I should. I worry a lot about being “correct”, particularly as my readership has increased over the past few years, and always endeavour to ensure my articles are as accurate as possible. It’s part of the reason why writing for others takes me so much longer than writing for myself. I always want to make sure the information I’m providing is 100% reliable. Even then, inevitably the odd mistake can slip through.</p>
<p>I’m grateful to be able to count on the superior knowledge of so many in the web development community. Plenty of online acquaintances have reached out with helpful pointers or corrections to articles over the years. I appreciate that they care enough to make the effort, and it has rarely felt hurtful or (worse) like mansplaining. In most cases it’s provided me with additional insight and helped me deepen my knowledge, and I try to correct my articles accordingly when I’ve got something wrong (as well as ackowledging the source of the correction).</p>
<p>I think most authors would want to be corrected when necessary, even if it feels a little bruising to the ego. That said, I’m a fan of giving feedback in the kindest way possible, and there are some approaches to correcting a person that are kinder than others. Because however kindly it’s meant, it can still feel kind of shitty when someone points out your flaws in public. Sure, you can tell yourself that they’re pointing out the flaws in your article/code/podcast/whatever, not your flaws as a human being. But it can still feel personal, and it takes time to build up resilience to criticism, however constructive.</p>
<p>And there are plenty of ways to give constructive feedback that <strong>don’t</strong> involve publicly shaming someone. You could email them. You could open a GitHub issue or PR. You could send them a DM. If you absolutely must take it public (such as on your favourite social media platform) then tone is everything. Think how someone on the receiving end might feel. Writing a short essay bulldozing their work is unlikely to make them feel good. But acknowledging the work they’ve put in is a far better approach. Something like “Great article! You might find [XYZ resource] interesting”, or “I have some additional information you might wish to consider. Do you mind if I email you?”</p>
<p>There are good reasons why the recipient of your feedback might prefer to keep the conversation public, but at least this way you’re giving them the choice.</p>
<p>Learning to take constructive feedback is a skill that can be worked on over time, but I don’t think the burden should solely be on the recipient. It’s a mark of our traditionally male-skewed workforce that the default response when someone is offended is to tell them to “be less sensitive”, rather than telling the person giving the feedback to be <strong>more</strong> sensitive. Learning to give constructive feedback with kindness and sensitivity is an important skill, and one that is too often overlooked. This applies to management, teaching, code reviews and more, just as much as it does to responding to someone’s writing.</p>
Logical Border Radius2022-12-15T00:00:00Zhttps://css-irl.info/logical-border-radius/<p>Confession: I almost never write <code>border-radius</code> as shorthand, largely because I can never remember the order. My brain is wired to remember margin and padding shorthands (top, right, bottom, left), but when it comes to corners it’s a different story. Do I start from the top left corner, or the top right? So I usually resort to the longhand, which is admittedly verbose, but at least has the advantage of being explicit, for me and any future readers of my code. For the record, these are equivalent:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">div</span> <span class="token punctuation">{</span>
<span class="token property">border-radius</span><span class="token punctuation">:</span> 10% 20% 30% 75%<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">div</span> <span class="token punctuation">{</span>
<span class="token property">border-top-left-radius</span><span class="token punctuation">:</span> 10%<span class="token punctuation">;</span>
<span class="token property">border-top-right-radius</span><span class="token punctuation">:</span> 20%<span class="token punctuation">;</span>
<span class="token property">border-bottom-right-radius</span><span class="token punctuation">:</span> 30%<span class="token punctuation">;</span>
<span class="token property">border-bottom-left-radius</span><span class="token punctuation">:</span> 75%<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>In the shorthand, we start from the top left corner, and go round clockwise, ending with the bottom left.</p>
<figure>
<img src="https://css-irl.info/logical-border-radius-02.webp" alt="Rounded rectangle showing the order of values in the border-radius shorthand" width="1400" height="700" />
</figure>
<p>Ironically, the very act of writing this article will probably ensure it’s firmly lodged in my brain forever more.</p>
<p>Using just two values in the shorthand we can set both the top-left and bottom-right radii (the first value), and the top-right and bottom-left (the second value).</p>
<figure>
<img src="https://css-irl.info/logical-border-radius-03.webp" alt="Rounded rectangle showing the result of two values for the shorthand" width="1400" height="700" />
</figure>
<h2>Unequal radii</h2>
<p>It gets a tad more complicated when dealing with unequal radii. Say we want a top left radius that’s more elliptical in appearance. We’ll need a longer border-radius along the top edge, and a shorter one on the left edge. We can use two values for our <code>border-top-left-radius</code> property:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">div</span> <span class="token punctuation">{</span>
<span class="token property">border-top-left-radius</span><span class="token punctuation">:</span> 100% 5rem<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>The first value is the radius is the horizontal direction, the second is in the vertical direction.</p>
<figure>
<img src="https://css-irl.info/logical-border-radius-05.webp" alt="Rectangle with rounded top left corner" width="1400" height="700" />
</figure>
<p>So how about using the shorthand for unequal radii? Setting all of our radii to different values can result in uneven, organic-looking shapes. We can use the slash separator, first specifying the horizontal radii for each corner, then the vertical radii after the slash.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">div</span> <span class="token punctuation">{</span>
<span class="token property">border-radius</span><span class="token punctuation">:</span> 2rem 8rem 8rem 9rem / 3rem 11rem 6rem 5rem<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Effectively, we take two passed around our element clockwise.</p>
<figure>
<img src="https://css-irl.info/logical-border-radius-04.webp" alt="Rectangle with unequally rounded corners" width="1400" height="700" />
</figure>
<p>We don’t have to set every radius in the shorthand. The shorthand in this example is equivalent to setting all of the vertical radii to 50%, while alternately setting the horizontal radii to 5rem and 2rem respectively.</p>
<pre class="language-css"><code class="language-css"><span class="token comment">/* Shortand */</span>
<span class="token selector">div</span> <span class="token punctuation">{</span>
<span class="token property">border-radius</span><span class="token punctuation">:</span> 5rem 2rem / 50%<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">/* Equivalent to: */</span>
<span class="token selector">div</span> <span class="token punctuation">{</span>
<span class="token property">border-top-left-radius</span><span class="token punctuation">:</span> 5rem 50%<span class="token punctuation">;</span>
<span class="token property">border-top-right-radius</span><span class="token punctuation">:</span> 2rem 50%<span class="token punctuation">;</span>
<span class="token property">border-bottom-right-radius</span><span class="token punctuation">:</span> 5rem 50%<span class="token punctuation">;</span>
<span class="token property">border-bottom-left-radius</span><span class="token punctuation">:</span> 2rem 50%<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h2>Logical properties for border-radius</h2>
<p>If we are working with different languages on our website we might want our border radii to change according to the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/writing-mode">writing mode</a> or <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/direction">direction</a> of our text. <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Logical_Properties">Logical properties</a> give us <strong>flow-relative</strong> values. For example for Arabic text, which is written right-to-left, the border radii might need to be applied on the left side of the element, while in European languages it would be applied on the right. Fortunately there are logical property equivalents for the longhand <code>border-radius-</code> properties.</p>
<p>Logical properties typically refer to the <strong>inline</strong> or <strong>block</strong> axis, which are different depending on the direction or writing mode. In English, which is written left-to-right, the inline axis refers to the horizontal axis and the block axis refers to the vertical axis. The <strong>start</strong> of the inline axis would be the left, the <strong>end</strong> would be the right. In a vertical writing mode, the inline axis would be the vertical axis, and the block axis would be the horizontal one.</p>
<p>The logical properties for border radius refer to the start and/or end of the inline or block axis. Of all the logical properties, they’re perhaps the most confusing to read. Let’s compare defining the top left, top right, bottom right and bottom left radii, and their logical property equivalents:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">div</span> <span class="token punctuation">{</span>
<span class="token property">border-top-left-radius</span><span class="token punctuation">:</span> 5rem<span class="token punctuation">;</span>
<span class="token property">border-top-right-radius</span><span class="token punctuation">:</span> 2rem<span class="token punctuation">;</span>
<span class="token property">border-bottom-right-radius</span><span class="token punctuation">:</span> 4rem<span class="token punctuation">;</span>
<span class="token property">border-bottom-left-radius</span><span class="token punctuation">:</span> 3rem<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">/* With logical properties: */</span>
<span class="token selector">div</span> <span class="token punctuation">{</span>
<span class="token property">border-start-start-radius</span><span class="token punctuation">:</span> 5rem<span class="token punctuation">;</span>
<span class="token property">border-start-end-radius</span><span class="token punctuation">:</span> 2rem<span class="token punctuation">;</span>
<span class="token property">border-end-end-radius</span><span class="token punctuation">:</span> 4rem<span class="token punctuation">;</span>
<span class="token property">border-end-start-radius</span><span class="token punctuation">:</span> 3rem<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<figure>
<img src="https://css-irl.info/logical-border-radius-01.webp" alt="Rounded rectangle showing equivalent logical and physical border-radius properties" width="1400" height="700" />
</figure>
<p>Getting used to writing <code>border-start-start-radius</code> is certainly a bit of a mental leap for me! It’s written like that because it’s the start of the block axis and the start of the inline axis, while <code>border-start-end-radius</code> refers to the start of the block axis and the end of the inline axis, and so on.</p>
<p>As you can see in the following demo, when the direction of the text is switched from left-to-right to right-to-left, as well as the order of items being reverse, the border radii are switched for the elements that use logical properties, whereas the ones using directional properties remain the same.</p>
<p class="codepen" data-height="458" data-default-tab="result" data-slug-hash="JjZgPzP" data-user="michellebarker" style="height: 458px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/JjZgPzP">
Logical border-radius</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<h3>Logical properties and unequal radii</h3>
<p>Somewhat confusingly, although the property name refers to the block axis then the inline axis, the radius value is written inline axis first, then block axis. This makes it consistent with the order of the physical properties (e.g. <code>border-top-right-radius</code> and <code>border-start-end-radius</code> would refer to the same corner, and the order of values would be the same), but might still be confusing! I suppose we could think of it as the block edge is the one that is getting the rounding treatment first, despite it referring to the horizontal radius.</p>
<h3>Vertical writing modes</h3>
<p>Here’s an interesting quirk: In a vertical writing mode, logical border radius properties don’t behave exactly as I would expect when using an elliptical border radius (i.e. more than one value). In <a href="https://codepen.io/michellebarker/pen/oNyrbNq">this demo</a>, I’m applying the <code>vertical-rl</code> writing mode to the second element.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.wrapper</span> <span class="token punctuation">{</span>
<span class="token property">writing-mode</span><span class="token punctuation">:</span> vertical-rl<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">p</span> <span class="token punctuation">{</span>
<span class="token property">border-start-start-radius</span><span class="token punctuation">:</span> 10rem 1rem<span class="token punctuation">;</span>
<span class="token property">border-start-end-radius</span><span class="token punctuation">:</span> 2rem 3rem<span class="token punctuation">;</span>
<span class="token property">border-end-end-radius</span><span class="token punctuation">:</span> 8rem 4rem<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.wrapper:nth-child(2)</span> <span class="token punctuation">{</span>
<span class="token property">writing-mode</span><span class="token punctuation">:</span> horizontal-tb<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p class="codepen" data-height="419" data-default-tab="result" data-slug-hash="oNyrbNq" data-user="michellebarker" style="height: 419px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/oNyrbNq">
Logical border-radius</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>As I would expect, what was the top left corner effectively becomes the top right corner as the element is now laid out vertically. But contrary to what I would expect, the two property values are reversed. The corner goes from a long, shallow curve along the <strong>block-start</strong> edge (the edge above the text) to a narrow, deep curve in a vertical writing mode. This surprised me, as I expected the corners to behave in much the same way as if I had rotated the element using a transform. In order to force the intended appearance, we need to reverse the values:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.wrapper:nth-child(2) p</span> <span class="token punctuation">{</span>
<span class="token comment">/* We have to reverse the values if we want them to be logical :( */</span>
<span class="token property">border-start-start-radius</span><span class="token punctuation">:</span> 1rem 10rem<span class="token punctuation">;</span>
<span class="token property">border-start-end-radius</span><span class="token punctuation">:</span> 3rem 2rem<span class="token punctuation">;</span>
<span class="token property">border-end-end-radius</span><span class="token punctuation">:</span> 4rem 8rem<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>I’m not sure whether I’m misunderstanding something in the spec or whether this is a bug (the fact that it works the same in Chrome and Firefox would suggest otherwise), but it seems counter-intuitive to me.</p>
<h2>No logical shorthand</h2>
<p>We’ve seen how to apply logical border radius properties, but it might surprise you to know there is no logical property shorthand. Unlike other border-related properties, if we want to apply a border radius using logical properties we must use the more verbose longhand options.</p>
<p>So be warned, when dealing with international content that could use different writing modes and text directions, using the logical properties for border radius might be a good idea. Using the <code>border-radius</code> shorthand, the physical values will remain the same regardless of the direction of flow.</p>
<h2>Reference</h2>
<p>Check out the <a href="https://css-tricks.com/almanac/properties/b/border-radius/">CSS Tricks almanac</a> for a useful guide to all things <code>border-radius</code>.</p>
Optimizing a Vue App2022-11-22T00:00:00Zhttps://css-irl.info/optimizing-a-vue-app/<p>Prioritizing performance when building our web apps improves the user experience and helps ensure they can be used by as many people as possible. In this article we’ll walk through some of the front-end optimization tips to keep our Vue apps as efficient as possible.</p>
<p><a href="https://www.smashingmagazine.com/2022/11/optimizing-vue-app/">Read the article</a></p>
Find Me On Mastodon2022-11-22T00:00:00Zhttps://css-irl.info/find-me-on-mastodon/<figure>
<img src="https://css-irl.info/find-me-on-mastodon-01.svg" alt="Hand drawn multicoloured Mastodon logos" />
</figure>
<p>Like many watching from the sidelines of Twitter’s rapid implosion, I’ve finally migrated to <a href="https://front-end.social/home">Mastodon</a>. You can find me at @michelle@front-end.social.</p>
<p>The reasons for leaving Twitter behind are many, and (hopefully) obvious. Twitter was never without its problems, but the consequences of it being run by an actual psychopath are already being felt. I was reluctant to have yet another social network to manage, but witnessing the mass exodus from Twitter of so many of the people I care about hearing from has led to my feed no longer feeling like the place it once was, as those voices gradually disappear. I guess I’ll still post on Twitter, for now, albeit less often. It’s a bit of a shame to leave behind the following that I’ve built up over the years of posting on this blog, but it’s not like my livelihood depends on it. For others I imagine it might be a different story.</p>
<p>Mastodon isn’t an out-and-out replacement, and I’m not sure it’ll ever be. Perhaps one of the consequences of this is we’ll curate our own, smaller communities in many different places rather than one, and perhaps that’s a good thing. I appreciate what Mastodon is doing, but all the language around “servers” and “instances” seems like it will inevitably alienate the less tech-savvy, and those people will go elsewhere. What I liked about my Twitter feed was seeing the mix of tech, design, politics, activism, climate journalism, niche art and web comics all in one place. Even my dad was on there. Over on Mastodon those things all feel more separate and less discoverable (or absent altogether), even though you can, I gather, follow people from different servers (I’m still learning). But perhaps that’s a good thing too, and starting afresh will mean my feed is more carefully curated. I won’t miss the ads either.</p>
<p>Over on Twitter I had a separate account for CSS stuff, but being on the front end server that seems less necessary right now (also, more admin). On the other hand, is it weird if I post non-front end things on the front end server? I don’t know. I’m still learning. For now, find me on here, or find me on Mastodon, or maybe find me on Twitter too. It already feels like a great front end community is building over on Mastodon, so come join us 🥰</p>
Web Sustainability Resources2022-11-14T00:00:00Zhttps://css-irl.info/web-sustainability-resources/<p>While there’s plenty to occupy the web community this week in the form of Twitter imploding, something far more important is also going on right now: the COP27 climate summit, taking place in Sharm El-Sheikh, where world leaders gather to hammer out agreements to (hopefully) limit catastrophic climate change.</p>
<p>There’s a lot of debate this year about corporate greenwashing, climate justice, and the ethics of holding the summit in a country where the government is known for human rights abuses and clamping down hard on peaceful protest, with many activists are boycotting the summit altogether. But regardless of the outcomes, we web developers should be aware of the ecological impact of our profession, and start examining our work through the lens of sustainability. So it seems timely to share a few resources that have crossed my radar this week.</p>
<h2>COP27 homepage sustainability review</h2>
<p>Last year, <a href="https://twitter.com/fershad">Fershad</a> analysed the climate impact of <a href="https://fershad.com/writing/cop26-a-quick-sustainability-check/">the COP26 homepage</a>. This year he’s taken <a href="https://fershad.com/writing/cop27-egypt-a-webpage-sustainability-review/">a look at the COP27 site</a>, albeit with a different slant — and showcasing some tools to help measure a website’s carbon footprint. Fershad’s contains lots of useful tips for making any website more sustainable.</p>
<p><a href="https://fershad.com/writing/cop27-egypt-a-webpage-sustainability-review/">Read Fershad’s analysis →</a></p>
<h2>Digital = Physical</h2>
<p><a href="https://medium.com/microsoft-design/digital-physical-4df9eceb63b2">This thoughtful article</a> by Jon Friedman and Rachel Romano from the Microsoft Design team does a great job of bringing home the physical impacts of our digital lifestyles (which we in wealthy countries are largely sheltered from), and really emphasises the importance of intersectionality. Microsoft’s <a href="https://wxcteam.microsoft.com/download/Microsoft-Green-Design-Principles.pdf">Green Design Principles</a> (referenced in the article) are worth reading too.</p>
<p><a href="https://medium.com/microsoft-design/digital-physical-4df9eceb63b2">Read the article</a></p>
<h2>Making WordPress Sustainable</h2>
<p>WordPress sites account for around 40% of the web, by some estimations. Members of the WordPress community have recently launched a Slack channel focused on sustainability. <a href="https://twitter.com/hanopcan">Hannah Smith</a> explains how sustainability means much more than web performance gains, and invites members of the community to contribute to the discussion.</p>
<p><a href="https://make.wordpress.org/project/2022/11/01/sustainability-channel-what-should-we-do/">Read Hannah’s article and get involved →</a></p>
<h2>Incremental Font Transfer</h2>
<p>Webfonts can be responsible for a large amount of data transfer on a website. I learnt this week (via the <a href="https://climateaction.tech/">ClimateAction.Tech</a> Slack group) that some font foundries don’t allow subsetting, which is one step we as developers can take to minimise the size of the font file. (<a href="https://subsetting.xyz/">Here’s a list</a> of foundries and details of whether they permit subsetting, if you need a quick reference.)</p>
<p>Through the ensuing discussing I also learnt about the <a href="https://www.w3.org/TR/IFT/">Incremental Font Transfer specification</a>. The spec proposes a new method of loading fonts incrementally, where portions of the font are loaded through multiple requests as and when needed. The specification is in working draft currently, but it’ll be great to see it progress into something we can use to improve the performance <strong>and</strong> the carbon footprint of our websites.</p>
<p><a href="https://www.w3.org/TR/IFT/">Read the spec →</a></p>
<h2>Astro</h2>
<p>I finally got around to playing around with <a href="https://astro.build/">Astro</a>, the hot new static site generator on the block, and I have to say, I’m pretty blown away. It’s a tool I’ve had my eye on for a while, but it’s only recently that it’s matured to a level where it’s (to me) worth the time investment.</p>
<p>In addition to being super-fast, lightweight and no-JS by default, it’s also really simple to get started with, with virtually no config required. I was able to get a simple site up and running within 20 minutes. All in all, it seems like a great tool for building more sustainable websites. I’ll definitely be using it for some side projects in the near future.</p>
<p><a href="https://astro.build/">Explore Astro →</a></p>
<h2>Wholegrain Digital Declutter</h2>
<p>This pleasingly-designed site by Wholegrain Digital offers a bunch of tips for decluttering your digital life, and reducing your carbon footprint in the process.</p>
<p><a href="https://www.wholegraindigital.com/digitaldeclutter/">Visit the site →</a></p>
<h2>Thinking about HTML and CSS for saving data</h2>
<p>As <a href="https://twitter.com/simevidas/status/1590704384454758400">Šime pointed out</a>, while the <code><picture></code> element enables us to serve up the most appropriate image depending on device size or resolution, we don’t have a way to tell the browser to serve a different image depending on bandwidth, or a user’s data preferences. It got me thinking about the <code>prefers-reduced-data</code> media query.</p>
<p>There are tons of reasons why a user might want to set their device preferences to data-saving mode. This media query would enable developers to say, conditionally load a webfont. Or we could turn off animations in our CSS when a user wants to save data, or set a darker colour scheme, as a kind of “eco-mode”. Arguably we need it now more than ever, but unfortunately no browser yet supports it.</p>
<p>This media query could also help us serve up appropriate images and media. The <code><picture></code> element allows for serving images depending on media conditions other than size — the following demo displays a darker image using the <code>prefers-color-scheme</code> media query.</p>
<p class="codepen" data-height="518" data-default-tab="result" data-slug-hash="LYrLvWQ" data-user="michellebarker" style="height: 518px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/LYrLvWQ">
Untitled</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>In theory, we could use <code>prefers-reduced-data</code> similarly, to serve a lower-resolution image to users who prefer it. Or perhaps serve a black-and-white, or halftone image?</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>picture</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>source</span> <span class="token attr-name">srcset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>low-res-image.webp<span class="token punctuation">"</span></span> <span class="token attr-name">media</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>(prefers-reduced-data: reduce)<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>high-res-image.webp<span class="token punctuation">"</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token attr-name">width</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1600<span class="token punctuation">"</span></span> <span class="token attr-name">height</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1068<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>picture</span><span class="token punctuation">></span></span></code></pre>
<p>Perhaps it’s time for us to start asking browser vendors to start prioritising this?</p>
<p><a href="https://w3c.github.io/csswg-drafts/mediaqueries-5/#prefers-reduced-data">Read the spec →</a></p>
Handy Tools For Mocking API Requests2022-10-25T00:00:00Zhttps://css-irl.info/handy-tools-for-mocking-api-requests/<p>In the course of my work I sometimes find myself needing to mock API requests — often when I’m prototyping, or testing a concept for an article. There are a couple of useful (free) tools I reach for on these occasions.</p>
<h2>n:point</h2>
<figure>
<img src="https://css-irl.info/handy-tools-for-mocking-api-requests-01_900.jpg" srcset="https://css-irl.info/handy-tools-for-mocking-api-requests-01_1600.jpg 1600w, https://css-irl.info/handy-tools-for-mocking-api-requests-01_900.jpg 900w" sizes="(max-width: 1080px) 90vw, 930px" alt="Screenshot of n:point website" />
</figure>
<p><a href="https://www.npoint.io/">n:point</a> provides lightweight, publicly accessible API endpoints for your JSON data. It’s super easy to use: simply give it some JSON and it sets up an endpoint in seconds. No messing about with access tokens, it’s ideal if you just want to fetch some data. You can also define and lock the JSON schema. I’m using n:point for a <a href="https://tympanus.net/codrops/2022/03/29/building-an-interactive-sparkline-graph-with-d3/">Codrops article</a> on creating an interactive line graph based on timeseries data.</p>
<h2>Mockeroo</h2>
<figure>
<img src="https://css-irl.info/handy-tools-for-mocking-api-requests-02_900.jpg" srcset="https://css-irl.info/handy-tools-for-mocking-api-requests-02_1600.jpg 1600w, https://css-irl.info/handy-tools-for-mocking-api-requests-02_900.jpg 900w" sizes="(max-width: 1080px) 90vw, 930px" alt="Screenshot of Mockeroo website" />
</figure>
<p>If you need to actually generate some data, <a href="https://mockaroo.com/">Mockeroo</a> is a tool that enables you to do just that. You can add rows of different types and Mockeroo will generate random data. There are configuration options, including setting ranges (for numbers and dates, for example), and including null values. If you’re happy writing Ruby, you can also include formulas and you can download the data in different formats.</p>
<p>I’ve only used Mockeroo for generating JSON, but you can also create API endpoints. It’s not as quick as n:point to set up, but looks useful if you need to mock any more complex API requests.</p>
Sustainable Web Development Strategies Within an Organization2022-10-11T00:00:00Zhttps://css-irl.info/sustainable-web-development-strategies-within-an-organization/<p>Climate change and sustainability are increasing concerns for digital organizations, as well as individuals working in tech. In this article for Smashing Magzine, we explore some of the ways we can raise awareness and effect change within an organization to create a more positive environmental impact.</p>
<p><a href="https://www.smashingmagazine.com/2022/10/sustainable-web-development-strategies-organization/">Read the article</a></p>
CSS Halftone Patterns2022-10-07T00:00:00Zhttps://css-irl.info/css-halftone-patterns/<p>A little while ago, <a href="https://codepen.io/thebabydino">Ana Tudor</a> created an impressive collection of <a href="https://codepen.io/thebabydino/pen/gOpMeWv">halftone patterns using only CSS</a>. As I had a little time to spare, I thought I’d dig into the code and see how it was done! Ana’s demos are made using Sass — what better way to learn than to try to produce similar effects using vanilla CSS?</p>
<figure>
<img src="https://css-irl.info/css-halftone-patterns-01a.jpg" alt="Multicoloured halftone pattern fading from top to bottom" width="1600" height="900" />
<figcaption>One of Ana Tudor’s beautiful halftone patterns created with CSS</figcaption>
</figure>
<p>Ana’s demo uses <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Houdini">Houdini</a> to animate the halftone background (only supported in Chromium browsers). For the purpose of this article we’re just going to be concentrating on the halftone pattern itself, rather than the animation.</p>
<h2>What is halftone?</h2>
<p>Halftone is defined as a reproduction of an image in which dark and light variations in tone are produced by variously sized dots. It was originally invented as a technique for printing images in journals and other publications at a time when other printing processes were prohibitively expensive. You might have come across the style in older comic books, where colour reproduction was limited.</p>
<figure>
<img src="https://css-irl.info/css-halftone-patterns-01b.jpg" alt="Black and white halftone image of a bluetit" width="1600" height="900" />
<figcaption>Photograph from Unsplash by Regine Tholen, converted to halftone</figcaption>
</figure>
<h2>Halftone in CSS</h2>
<p>It’s simple enough to produce a dotted background in CSS by using <code>radial-gradient()</code>. But for a halftone effect, we need some variation in the size of the dots in order to depict an image or pattern.</p>
<p>To get this effect, we need three essential ingredients:</p>
<ol>
<li>A repeating background, using <code>radial-gradient</code>.</li>
<li>A mask, which will be the image or pattern we want to reproduce.</li>
<li>The secret weapon: the contrast filter. We’re going to take our radial gradient background and turn the contrast up high. The effect when combined with the mask is where the magic happens.</li>
</ol>
<h2>Simple dotted background</h2>
<p>Let’s start with a simple dotted background, made with <code>radial-gradient()</code>. First let’s define the gradient. Instead of a smooth gradient where one colour blends into another, we’re using hard stops, so the colour goes from black to transparent without any blending. That’s how we get a circle with a smooth edge.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.halftone</span> <span class="token punctuation">{</span>
<span class="token property">background</span><span class="token punctuation">:</span> <span class="token function">radial-gradient</span><span class="token punctuation">(</span>circle at center<span class="token punctuation">,</span> black 0.25rem<span class="token punctuation">,</span> transparent 0<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>We’ll have just a single circle at this point. If we change the background size to something smaller, we’ll instead get a repeating dotted pattern. We can use <code>background-repeat: round</code> to ensure the dots won’t be clipped, and we’ll position our background in the centre. I prefer to use separate properties as it’s easier to read, but you can use the <code>background</code> <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/background">shorthand</a>.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.halftone</span> <span class="token punctuation">{</span>
<span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token function">radial-gradient</span><span class="token punctuation">(</span>
circle at center<span class="token punctuation">,</span>
black 0.25rem<span class="token punctuation">,</span>
transparent 0
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">background-size</span><span class="token punctuation">:</span> 1rem 1rem<span class="token punctuation">;</span>
<span class="token property">background-repeat</span><span class="token punctuation">:</span> round<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>This gives us a straightforward dotted grid. If we want an angled grid instead we could use two gradients overlaid, as long as the second colour is transparent (otherwise we wouldn’t see the gradient below).</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.halftone</span> <span class="token punctuation">{</span>
<span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token function">radial-gradient</span><span class="token punctuation">(</span>
circle at center<span class="token punctuation">,</span>
black 0.25rem<span class="token punctuation">,</span>
transparent 0
<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">radial-gradient</span><span class="token punctuation">(</span>circle at center<span class="token punctuation">,</span> black 0.25rem<span class="token punctuation">,</span> transparent 0<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">background-size</span><span class="token punctuation">:</span> 1.3rem 1.3rem<span class="token punctuation">;</span>
<span class="token property">background-position</span><span class="token punctuation">:</span> 0 0<span class="token punctuation">,</span> 0.65rem 0.65rem<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>The result is subtly different.</p>
<figure>
<img src="https://css-irl.info/css-halftone-patterns-01.jpg" alt="Dotted backgrounds (black dots on white) using regular grid (left) and angled grid (right)" width="1600" height="900" />
</figure>
<p>We’re repeating several values here, so we could make our code a bit more concise (and readable) using custom properties:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.halftone</span> <span class="token punctuation">{</span>
<span class="token property">--dotSize</span><span class="token punctuation">:</span> 0.25rem<span class="token punctuation">;</span>
<span class="token property">--bgSize</span><span class="token punctuation">:</span> 1.35rem<span class="token punctuation">;</span>
<span class="token property">--bgPosition</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--bgSize<span class="token punctuation">)</span> / 2<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token function">radial-gradient</span><span class="token punctuation">(</span>
circle at center<span class="token punctuation">,</span>
black <span class="token function">var</span><span class="token punctuation">(</span>--dotSize<span class="token punctuation">)</span><span class="token punctuation">,</span>
transparent 0
<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">radial-gradient</span><span class="token punctuation">(</span>circle at center<span class="token punctuation">,</span> black <span class="token function">var</span><span class="token punctuation">(</span>--dotSize<span class="token punctuation">)</span><span class="token punctuation">,</span> transparent 0<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">background-size</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--bgSize<span class="token punctuation">)</span> <span class="token function">var</span><span class="token punctuation">(</span>--bgSize<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">background-position</span><span class="token punctuation">:</span> 0 0<span class="token punctuation">,</span> <span class="token function">var</span><span class="token punctuation">(</span>--bgPosition<span class="token punctuation">)</span> <span class="token function">var</span><span class="token punctuation">(</span>--bgPosition<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Using custom properties means it’ll be much easier to adjust individual values later on, as we refine our halftone pattern: we’ll have just one place to update them, instead of a bunch of places.</p>
<p><a href="https://codepen.io/michellebarker/pen/wvjpbMe">See the demo</a></p>
<h2>Masking</h2>
<p>To create a halftone pattern we’ll need to move our background properties to a pseudo-element. This is because shortly we’re going to apply a contrast filter to the element itself.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.halftone</span> <span class="token punctuation">{</span>
<span class="token property">--dotSize</span><span class="token punctuation">:</span> 0.25rem<span class="token punctuation">;</span>
<span class="token property">--stop1</span><span class="token punctuation">:</span> 3%<span class="token punctuation">;</span>
<span class="token property">--stop2</span><span class="token punctuation">:</span> 60%<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.halftone::after</span> <span class="token punctuation">{</span>
<span class="token comment">/* Cover our element */</span>
<span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">''</span><span class="token punctuation">;</span>
<span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span>
<span class="token property">inset</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token comment">/* Dotted background */</span>
<span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token function">radial-gradient</span><span class="token punctuation">(</span>
circle at center<span class="token punctuation">,</span>
black <span class="token function">var</span><span class="token punctuation">(</span>--dotSize<span class="token punctuation">)</span><span class="token punctuation">,</span>
transparent 0
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">background-size</span><span class="token punctuation">:</span> 1.3rem 1.3rem<span class="token punctuation">;</span>
<span class="token property">background-position</span><span class="token punctuation">:</span> 0 0<span class="token punctuation">,</span> 0.65rem 0.65rem<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>The next step in creating our halftone pattern is to add a mask. To start with, we’ll use a simple linear gradient, fading black to transparent from top to bottom.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.halftone::after</span> <span class="token punctuation">{</span>
<span class="token property">mask-image</span><span class="token punctuation">:</span> <span class="token function">linear-gradient</span><span class="token punctuation">(</span><span class="token function">rgb</span><span class="token punctuation">(</span>0 0 0<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">rgb</span><span class="token punctuation">(</span>0 0 0 / 0<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>(Older versions of Safari require prefixed mask properties, e.g. <code>-webkit-mask-image</code>. I’m excluding them here for brevity.)</p>
<figure>
<img src="https://css-irl.info/css-halftone-patterns-02.jpg" alt="The two dotted patterns with mask applied, giving them the appearance of fading from top to bottom" width="1600" height="900" />
</figure>
<p>Applying the mask doesn’t result in a halftone pattern, as the dots are all the same size — they simply appear to become more transparent from top to bottom. We’ll need to revisit our background pattern to change that.</p>
<h2>Blurring the background</h2>
<p>Instead of using hard stops for our radial gradient background, we’re going to give them smooth edges by fading them, more like a typical gradient. We’ll need two stops: the point at which the black begins to fade (<code>--stop1</code>) and the point at which it should be fully transparent (<code>--stop2</code>). The exact values don’t matter too much for now, as we can always adjust them later — again, custom properties make it easy to do this.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.halftone</span> <span class="token punctuation">{</span>
<span class="token property">--stop1</span><span class="token punctuation">:</span> 0.06rem<span class="token punctuation">;</span>
<span class="token property">--stop2</span><span class="token punctuation">:</span> 0.65rem<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.halftone::after</span> <span class="token punctuation">{</span>
<span class="token property">background-image</span><span class="token punctuation">:</span> <span class="token function">radial-gradient</span><span class="token punctuation">(</span>
circle at center<span class="token punctuation">,</span>
black <span class="token function">var</span><span class="token punctuation">(</span>--stop1<span class="token punctuation">)</span><span class="token punctuation">,</span>
transparent <span class="token function">var</span><span class="token punctuation">(</span>--stop2<span class="token punctuation">)</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<figure>
<img src="https://css-irl.info/css-halftone-patterns-03.jpg" alt="The two dotted patterns with softer gradient, making them appear blurred" width="1600" height="900" />
</figure>
<p>You’ll notice now that our background now looks nothing like a halftone. But it’ll all come together in the final step.</p>
<h2>Adding contrast</h2>
<p>Lastly, we add a <code>contrast</code> filter to our element (<strong>not</strong> the pseudo-element). A background colour is also crucial, otherwise we won’t see the contrast.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.halftone</span> <span class="token punctuation">{</span>
<span class="token property">background</span><span class="token punctuation">:</span> white<span class="token punctuation">;</span>
<span class="token property">filter</span><span class="token punctuation">:</span> <span class="token function">contrast</span><span class="token punctuation">(</span>50<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>The goal here is to add enough contrast so that our radial gradient background no longer appears blurred, but starts to resemble the dotted pattern we had originally — except now you should notice that the dots vary in size, depending on to what degree they are masked by the linear gradient. If you don’t see much contrast at this point, try adjusting the values of the radial gradient stops (in the previous step). The larger the difference between <code>--stop</code> and <code>--stop3</code>, the greater the variation in dot size.</p>
<p>The faded effect is currently a little abrupt, so let’s adjust the mask to fade to a less transparent value: about 0.5 on the alpha channel should do it, ensuring we don’t end up with some areas with no dots at all.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.halftone::after</span> <span class="token punctuation">{</span>
<span class="token property">mask-image</span><span class="token punctuation">:</span> <span class="token function">linear-gradient</span><span class="token punctuation">(</span><span class="token function">rgb</span><span class="token punctuation">(</span>0 0 0<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">rgb</span><span class="token punctuation">(</span>0 0 0 / 0.5<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<figure>
<img src="https://css-irl.info/css-halftone-patterns-04.jpg" alt="The two dotted patterns with a pleasing halftone effect, from larger dots at the top to smaller ones at the bottom" width="1600" height="900" />
</figure>
<p>Here’s the full demo.</p>
<p class="codepen" data-height="522" data-default-tab="result" data-slug-hash="eYrKrKJ" data-user="michellebarker" style="height: 522px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/eYrKrKJ">
Simple halftone backgrounds</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<h2>Colouring the background with blend modes</h2>
<p>We now have a black and white halftone pattern, but we might want to add some colour. There’s one problem: changing the background colour of our element will result in a distorted effect, because of the contrast filter. (Try changing the background value of the <code>.halftone</code> class in the above demo to a light colour to see the problem.)</p>
<p>We can get around this by using <code>mix-blend-mode</code>. Let’s add a background to our <code>body</code>. (We could alternatively wrap each element, as Ana does in her demo.)</p>
<pre class="language-css"><code class="language-css"><span class="token selector">body</span> <span class="token punctuation">{</span>
<span class="token property">background</span><span class="token punctuation">:</span> <span class="token function">linear-gradient</span><span class="token punctuation">(</span>45deg<span class="token punctuation">,</span> red<span class="token punctuation">,</span> blue<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Then we’ll apply the <code>multiply</code> blend mode to the halftone element. This will result in the white areas being rendered transparent, while the dots remain black. The <code>screen</code> blend mode has the opposite effect: the white background is preserved, and the black dots will instead show the background beneath.</p>
<figure>
<img src="https://css-irl.info/css-halftone-patterns-05.jpg" alt="The two halftone patterns on a red to blue gradient background" width="1600" height="900" />
<figcaption>With the <code>multiply</code> blend mode</figcaption>
</figure>
<figure>
<img src="https://css-irl.info/css-halftone-patterns-06.jpg" alt="The two halftone patterns as gradient dots on a white background." width="1600" height="900" />
<figcaption>With the <code>screen</code> blend mode</figcaption>
</figure>
<p>We could alternatively render a black background with the dots the colour of the background, by inverting the colours of the backgrounds. We can either switch these colours from black to white (and vice versa), or we could use the <code>invert</code> filter, as I’m doing here.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.halftone</span> <span class="token punctuation">{</span>
<span class="token property">filter</span><span class="token punctuation">:</span> <span class="token function">contrast</span><span class="token punctuation">(</span>50<span class="token punctuation">)</span> <span class="token function">invert</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--invert<span class="token punctuation">,</span> 0<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.halftone--inverted</span> <span class="token punctuation">{</span>
<span class="token property">--invert</span><span class="token punctuation">:</span> 1<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>This demo shows the different effects produced by different combinations of blend modes and the <code>invert</code> filter.</p>
<p class="codepen" data-height="532" data-default-tab="result" data-slug-hash="YzLvvbm" data-user="michellebarker" style="height: 532px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/YzLvvbm">
Halftone background with blend modes</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<h2>Different mask effects</h2>
<p>We’re using a simple linear gradient as a mask, but there are endless possibilities for interesting, creative effects. We could use radial gradients, conic gradients, repeating gradients (or a combination!). We can use images too: Either SVGs or transparent PNGs. This demo shows a few different effects. You can also adjust a few custom properties using the controls to explore some different options.</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p class="codepen" data-height="525" data-default-tab="result" data-slug-hash="yLjExeW" data-user="michellebarker" style="height: 525px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/yLjExeW">
Halftone pattern explorer</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>Ana has <a href="https://codepen.io/thebabydino/pen/LYGGwrm">another demo</a> that applies a halftone treatment to non-transparent images. We’re not going to walk through it here, but it’s another intriguing one to study!</p>
<h2>Limitations</h2>
<p>Because we’re using a filter and blend mode, either our dots <strong>or</strong> the background ideally need to be white or black. If we try applying a different colour we’re not always going to get the result we hope for, as the colour will be distorted. Perhaps there’s another way to do it? Still, it’s interesting to know we can create these kind of effects with only CSS!</p>
Web Sustainability and the Ethical Dilemma2022-09-19T00:00:00Zhttps://css-irl.info/web-sustainability-and-the-ethical-dilemma/<p>Last week I had the privilege of participating in <a href="https://smashingconf.com/freiburg-2022">Smashing Conference in Freiburg</a>. One of the standout sessions was from <a href="https://asim.dev/">Asim Hussain</a>, of the <a href="https://greensoftware.foundation/">Green Software Foundation</a>, who talked about what a Net-Zero strategy means for organisations building websites. It was interesting to hear about the challenges of measuring the carbon emissions of a website, and the many different aspects that must be considered while doing so.</p>
<p>Asim pointed out that offsets are not an effective way of getting to Net-Zero, which might seem controversial to some, given that many companies that claim to be carbon neutral are at least partly counting carbon offsetting towards that goal. I’m inclined to agree with Asim here. Offsetting feels (to me) a lot like creative accounting: in the best case simply moving carbon around the system, without actually contributing to the zero-carbon goal.</p>
<p>It makes choosing an electricity supplier, or hosting provider a more difficult task if you want to be sure of their green credentials. In theory choosing a green web host should make a big difference to how clean your website is. If they exclusively use renewable energy, the carbon footprint should be very small. But many don’t explicitly make it clear how much of their trumpeted green claims are achieved through offsetting.</p>
<aside>
<h2 class="heading-3">Green hosting</h2>
<p>If you’re looking to switch to a green web host, I’ve seen <a href="https://www.leaf.cloud/">Leaf Cloud</a> and <a href="https://krystal.uk/green">Krystal</a> recommended by people working in digital sustainability. The Green Web Foundation also has a <a href="https://www.thegreenwebfoundation.org/directory/">green hosting directory</a>.</p>
</aside>
<p>Another illuminating talk came from web performance expert <a href="https://csswizardry.com/">Harry Roberts</a>, who shared some useful insights for how optimising your <code><head></code> element can result in big performance gains. I’m always in favour of us developers doing whatever we can to make websites faster for our users, and often web performance and sustainability make great bedfellows. Reducing data transfer and CPU load inevitably reduces the carbon emissions generated by our website. As well as saving energy serving data to the user, we can also improve the longevity of devices, meaning they will need to be replaced less often. Device manufacturing and disposal uses a huge amount of energy and natural resources, and generates a lot of waste.</p>
<p>But is it always the case that faster websites are greener websites? We reluctantly have to consider another facet: if making a website for a car manufacturer faster leads to an increase in the number of cars sold, can we really say that our website is greener?</p>
<p>It applies to websites for many industries, of course: fast fashion, electrical goods, airlines and many more. Working in tech we’re accustomed to thinking of what we do as “clean” because we can’t see the carbon emissions. But in reality we service some of the dirtiest industries on the planet. We can try to justify it to ourselves in all sorts of ways, but in the end this is perhaps a problem without a technological solution, something that makes us developers extremely uncomfortable: to choose not to build.</p>
<p>Often web performance improvements are framed in terms of saving a company <em>x</em> amount of money or resulting in increased turnover, sometimes totalling millions of dollars, through better, faster user experience. But to frame it from a purely ecological perspective, is this really a better outcome when the end result is enabling environmental destruction on a greater scale? I’m not saying we should endeavour to make websites <strong>slower</strong>. Faster websites are nearly always better than the alternative. But we shouldn’t be afraid to ask ourselves some uncomfortable questions about whose needs we’re servicing.</p>
<p>That’s not to say you can’t do any good if you’re working for a company that is contributing in some way to climate change (and let’s face it, that’s probably most companies). Many people don’t have a choice, and there’s something to be said for being able to influence change from the inside. But in discussions of digital sustainability, it sometimes feels like the elephant in the room.</p>
<h2>Accessibility and green software</h2>
<p>While we’re talking about sustainability, Asim and a few others recorded a thought-provoking podcast episode on <a href="https://podcast.greensoftware.foundation/e/lnqrrk98-accessibility-and-green-software">accessibility and green software</a>, including exploring what’s driving increased interested in green software, and what those interested in digital sustainability can learn from the sustainability space.</p>
<p><a href="https://podcast.greensoftware.foundation/e/lnqrrk98-accessibility-and-green-software">Listen to the podcast</a></p>
<h2>Sustainable Web Design W3C community group</h2>
<p>If you’re interested in helping drive the increased adoption of sustainability principles on the web, a W3C <a href="https://www.w3.org/community/sustyweb/">community group</a> has been created with the goal of making the process of creating digital products more sustainable, and creating products that encourage better practices for users.</p>
<p>It’s important to ensure a diverse range of voices are represented when creating new guidelines. <a href="https://www.w3.org/community/sustyweb/">Join the group</a> and help be part of the solution.</p>
Detecting CSS Selector Support2022-09-13T00:00:00Zhttps://css-irl.info/detecting-css-selector-support/<p>You might already be aware of how we can use <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@supports">feature detection</a> in CSS to check whether a particular property and value combination is supported. This is a pretty smart way to go about writing robust CSS that caters for users with a whole range of browsers and devices with different capabilities, and is infinitely preferable to <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent">user agent sniffing</a>. We can check whether a browser supports <code>aspect-ratio</code>, say, and provide a fallback in cases where lack of support would hamper the user experience.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.some-element</span> <span class="token punctuation">{</span>
<span class="token property">max-height</span><span class="token punctuation">:</span> 200px<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token atrule"><span class="token rule">@supports</span> <span class="token punctuation">(</span><span class="token property">aspect-ratio</span><span class="token punctuation">:</span> 1<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token selector">.some-element</span> <span class="token punctuation">{</span>
<span class="token property">aspect-ratio</span><span class="token punctuation">:</span> 1<span class="token punctuation">;</span>
<span class="token property">max-height</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>This particular block of code sets a max-height on an element, except when <code>aspect-ratio</code> is supported, when we’ll use that instead.</p>
<p>We could make this a bit more concise by using the <code>not</code> keyword, which essentially reverses the above: We’ll only set a max-hight when <code>aspect-ratio</code> is <strong>not</strong> supported.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.some-element</span> <span class="token punctuation">{</span>
<span class="token property">aspect-ratio</span><span class="token punctuation">:</span> 1<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token atrule"><span class="token rule">@supports</span> <span class="token keyword">not</span> <span class="token punctuation">(</span><span class="token property">aspect-ratio</span><span class="token punctuation">:</span> 1<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token selector">.some-element</span> <span class="token punctuation">{</span>
<span class="token property">max-height</span><span class="token punctuation">:</span> 200px<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>The only problem here is that very old browsers that <strong>don’t</strong> support feature queries (as well as not supporting <code>aspect-ratio</code>) won’t get the fallback. This is much less of a problem than it used to be, given that feature queries have been well-supported for some time, but worth bearing in mind nonetheless, especially if a lot of your users might be using older devices.</p>
<p>It might sound obvious, but it’s worth noting: For <code>@supports</code> to evaluate true, both the property name <strong>and</strong> the value must be supported. Neither of the expressions inside the parentheses here are valid, meaning that any styles inside the <code>@supports</code> statements will never be applied:</p>
<pre class="language-css"><code class="language-css"><span class="token comment">/* Invalid value for aspect-ratio */</span>
<span class="token atrule"><span class="token rule">@supports</span> <span class="token punctuation">(</span><span class="token property">aspect-ratio</span><span class="token punctuation">:</span> red<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token punctuation">}</span>
<span class="token comment">/* Missing value */</span>
<span class="token atrule"><span class="token rule">@supports</span> <span class="token punctuation">(</span>aspect-ratio<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token punctuation">}</span></code></pre>
<p>We can also combine conditions using the <code>and</code> and <code>or</code> operators, much the same as we might write a media query. The following will apply an aspect ratio only when the browser supports both <code>aspect-ratio</code> and <code>rotate</code> (one of the <a href="https://web.dev/css-individual-transform-properties/">new CSS transform properties</a>):</p>
<pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@supports</span> <span class="token punctuation">(</span><span class="token property">aspect-ratio</span><span class="token punctuation">:</span> 1<span class="token punctuation">)</span> <span class="token keyword">and</span> <span class="token punctuation">(</span><span class="token property">rotate</span><span class="token punctuation">:</span> 30deg<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token selector">.some-element</span> <span class="token punctuation">{</span>
<span class="token property">aspect-ratio</span><span class="token punctuation">:</span> 1<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<h2>Detecting selector support</h2>
<p>CSS has given us some pretty cool selectors recently in the form of pseudo-elements and pseudo-classes. For example, <code>:focus-visible</code> allows us to style an element when it’s <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible">focused with a keyboard</a>. Support for <code>:has()</code> has recently landed in Chrome and Safari, which allows us to apply styles to an element as a result of its children.</p>
<p>Happily, we can detect support for these selectors using <code>@supports</code>, by prefixing the parentheses with <code>selector</code>. We might want to change the focus style of button when it receives focus from a mouse, but keep the default focus ring when focused with a keyboard, and if the browser does not support <code>:focus-visible</code>.</p>
<pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@supports</span> <span class="token function">selector</span><span class="token punctuation">(</span><span class="token selector-function-argument selector">:focus-visible</span><span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token selector">button:focus:not(:focus-visible)</span> <span class="token punctuation">{</span>
<span class="token property">outline</span><span class="token punctuation">:</span> 2px solid limegreen<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>(This complex selector doesn’t seem to work in Safari for some reason, despite Safari supporting both <code>@supports selector()</code> <strong>and</strong> <code>:focus-visible</code>.)</p>
<aside>You know what I discovered in the course of writing this article? Browsers no longer seem to do that thing where they apply a focus ring to an element focused with a mouse. Not sure when that happened, but it seems like a primary case for <code>:focus-visible</code> is now resolved by the browsers themselves 🤷♀️</aside>
<p>There appears to be a slight discrepancy between browser implementations when using more complex pseudo-classes such as <code>:has()</code>: Currently Safari requires <code>:has()</code> to include some other selector, but Chrome does not.</p>
<pre class="language-css"><code class="language-css"><span class="token comment">/* This works in Chrome but not Safari */</span>
<span class="token atrule"><span class="token rule">@supports</span> <span class="token function">selector</span><span class="token punctuation">(</span><span class="token selector-function-argument selector">:has()</span><span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token punctuation">}</span>
<span class="token comment">/* This works everywhere */</span>
<span class="token atrule"><span class="token rule">@supports</span> <span class="token function">selector</span><span class="token punctuation">(</span><span class="token selector-function-argument selector">:has(.some-element)</span><span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token punctuation">}</span></code></pre>
<h2>Browser support</h2>
<p><code>@supports selector()</code> is supported in all modern browsers, but only in the past year or so. As ever, it’s worth considering users of older browsers, and using it with care as part of your progressive enhancement strategy.</p>
Creative CSS Layout (and the Flexible Web)2022-08-31T00:00:00Zhttps://css-irl.info/creative-css-layout-and-the-flexible-web/<p>Hi friends! Way back in June (which already feels like such a long time ago in CSS land!) I had the pleasure of speaking about CSS layout at <a href="https://cssday.nl/2022">CSS Day conference</a>. I’m pleased to say the video has now been published! Take a look to learn about a whole bunch of modern CSS layout techniques, with a few little tricks thrown in.</p>
<p>https://www.youtube.com/embed/tueTFd2TQUA</p>
<p>I attempted to pack a lot into this talk, but what I tried to emphasise is that many of the new CSS features we have at our disposal help (and even encourage) us to embrace the flexible web of the present day. One where we can’t possibly predict what kind of devices our user is browsing our website on, but where we can ensure our layouts are robust across <strong>all</strong> devices, by eschewing pixel-perfection in favour of something more intrinsic to the web. <strong>Material honesty</strong>, as <a href="https://www.youtube.com/watch?v=CdZZcbZG83o">Jeremy talked about</a>. Diversity is the web’s strength, not its limitation.</p>
<h2>Container queries are here!</h2>
<p>Speaking of CSS layout, <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Container_Queries">Container Queries</a> have finally landed in the latest (stable) version of Chrome! Miriam Suzanne has a great <a href="https://www.oddbird.net/2022/08/18/cq-syntax/">write-up covering the syntax</a>.</p>
<p>There’s also a <a href="https://github.com/GoogleChromeLabs/container-query-polyfill">polyfill</a> by the Chrome team, so you can use it now!</p>
<h2>:has() too!</h2>
<p>As if that wasn’t enough exciting CSS layout news, both Chrome and Safari have shipped support for <code>:has()</code>, AKA “the parent selector”! Jen Simmons <a href="https://webkit.org/blog/13096/css-has-pseudo-class/">has the lowdown here</a>, and Una Kravets covers both features for the <a href="https://developer.chrome.com/blog/has-with-cq-m105/">Chrome Developer Blog</a>.</p>
<h2>On flexibility</h2>
<p>Somewhat related to the flexibility of the web, I enjoyed reading a recent blog post exchange between <a href="https://twitter.com/davatron5000">Dave Rupert</a> and <a href="https://twitter.com/m_ott">Matthias Ott</a>. Dave laments the increasingly wild range of jobs thrust onto the shoulders of front end developers. They both talk about “bridge” roles, and Matthias points out that an evolution in design thinking is needed now more than ever, when we take into account all the flexibility that CSS now provides us with (see above!).</p>
<p>I love to see these conversations play out, even if it may be some time before we agree on the solutions.</p>
<ul>
<li><a href="https://daverupert.com/2022/08/web-is-a-harsh-manager/">The Web is a Harsh Manager</a> by Dave</li>
<li><a href="https://matthiasott.com/notes/better-bridges">Better Bridges</a> by Matthias</li>
</ul>
<h2>Smashing Conference Freiburg 2022</h2>
<p>Want to learn all about the latest and greatest CSS layout features? Come along to <a href="https://smashingconf.com/freiburg-2022">Smashing Conference</a> in Freiburg, 5–7 September! We’ll explore container queries and much more, live on stage!</p>
Creative List Styling2022-08-24T00:00:00Zhttps://css-irl.info/creative-list-styling/<p>What comes to mind when you think of a list? The most obvious example is a shopping list—the most simple of lists, a collection of items in no particular order. But we use lists in all sorts of ways on the web. A collection of upcoming concerts at a venue? Very likely a list. A multi-step booking process? Quite possibly a list. A gallery of images? Even that could be considered a list of images with captions.</p>
<p>In this article we'll dive into the different HTML list types available to us on the web and when to use them, including some attributes you might not be familiar with. We'll also take a look at some useful and creative ways to style them with CSS.</p>
<p><a href="https://web.dev/creative-list-styling/">Read the article</a></p>
A Handy Use For Cascade Layers2022-08-23T00:00:00Zhttps://css-irl.info/a-handy-use-for-cascade-layers/<p>I was just preparing a demo for an <a href="https://smashingconf.com/freiburg-2022">upcoming talk</a> and it suddenly occurred to me that cascade layers would be a perfect solution to a problem I was having.</p>
<p>For any element of live coding, I like to meticulously prepare my demos to prevent any moments of friction on the day. I wanted to apply a bunch of default styles to a grid of cards in <a href="https://codepen.io/michellebarker/pen/QWmPGqL">this container queries demo</a>, but I wanted those styles to appear at the bottom of the stylesheet, so that when it came to live coding the demo I could write all the relevant code at the top, and the audience could just focus on the layout styles I would be discussing. However, there were a few styles that consequently wouldn’t be applied, as they would have been overridden by those further down. For example, at the top of the CSS file I want to apply a container query that would result in a horizontal card layout:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.grid__item-wrapper</span> <span class="token punctuation">{</span>
<span class="token property">container</span><span class="token punctuation">:</span> card / inline-size<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token atrule"><span class="token rule">@container</span> card <span class="token punctuation">(</span>inline-size > 24em<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token selector">.card</span> <span class="token punctuation">{</span>
<span class="token property">flex-direction</span><span class="token punctuation">:</span> row<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.card img</span> <span class="token punctuation">{</span>
<span class="token property">width</span><span class="token punctuation">:</span> 40%<span class="token punctuation">;</span>
<span class="token property">flex</span><span class="token punctuation">:</span> 1 1 auto<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>But further down in the stylesheet I was setting a flex direction of <code>column</code> on the card:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.card</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span>
<span class="token property">flex-direction</span><span class="token punctuation">:</span> column<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Container queries themselves don’t increase specificity. I could write the following, and the <code>body</code> would always be blue:</p>
<pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@container</span> layout <span class="token punctuation">(</span>inline-size > 40em<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token selector">body</span> <span class="token punctuation">{</span>
<span class="token property">background</span><span class="token punctuation">:</span> red<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token selector">body</span> <span class="token punctuation">{</span>
<span class="token property">background</span><span class="token punctuation">:</span> blue<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>I could increase the specificity of the selector at the top of the file, but I didn’t like my chances of remembering to do that in the midst of a live coding session! <a href="https://css-irl.info/a-handy-use-for-cascade-layers/">Cascade Layers</a> are a perfect solution. We can specify the order of layers at the top of the file:</p>
<pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@layer</span> general<span class="token punctuation">,</span> layout<span class="token punctuation">;</span></span></code></pre>
<p>Then put our relevant styles inside them:</p>
<pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@layer</span> layout</span> <span class="token punctuation">{</span>
<span class="token comment">/* Only the styles relevant to the demo go in this layer */</span>
<span class="token selector">.grid__item-wrapper</span> <span class="token punctuation">{</span>
<span class="token property">container</span><span class="token punctuation">:</span> card / inline-size<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token atrule"><span class="token rule">@container</span> card <span class="token punctuation">(</span>inline-size > 24em<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token selector">.card</span> <span class="token punctuation">{</span>
<span class="token property">flex-direction</span><span class="token punctuation">:</span> row<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.card img</span> <span class="token punctuation">{</span>
<span class="token property">width</span><span class="token punctuation">:</span> 40%<span class="token punctuation">;</span>
<span class="token property">flex</span><span class="token punctuation">:</span> 1 1 auto<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token atrule"><span class="token rule">@layer</span> general</span> <span class="token punctuation">{</span>
<span class="token comment">/* CSS reset and general styles not relevant to the demo go here */</span>
<span class="token punctuation">}</span></code></pre>
<p>This means the styles in the <code>general</code> layer will be applied first, despite being below those in the <code>layout</code> layer.</p>
<figure>
<img src="https://css-irl.info/a-handy-use-for-cascade-layers.jpg" alt="Container queries demo of a grid of cards in Codepen showing the order in which layers are applied in the CSS panel" />
</figure>
<p>Here’s the full demo:</p>
<p class="codepen" data-height="457.3125" data-default-tab="result" data-slug-hash="QWmPGqL" data-user="michellebarker" style="height: 457.3125px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/QWmPGqL">
Container queries + cascade layers</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<h2>Browser support</h2>
<p>I was surprised by how widespread <a href="https://caniuse.com/?search=cascade%20layers">browser support</a> already is for Cascade Layers — over 87% of browsers worldwide now support them. But, needless to say, an application that relies on Cascade Layers would not fare well in browsers that don’t support them. Happily there’s a <a href="https://www.oddbird.net/2022/06/21/cascade-layers-polyfill/">PostCSS polyfill</a> (which I haven’t tried out yet).</p>
<h2>Caveats</h2>
<p>The one drawback to using Cascade Layers here is that it seems to result in the <a href="https://github.com/GoogleChromeLabs/container-query-polyfill">container queries polyfill</a> no longer working. It’s not a big deal for me in this instance as I’ll be using Chrome, but running the demo in Firefox means you won’t see the container queries in action. I’ve filed <a href="https://github.com/GoogleChromeLabs/container-query-polyfill/issues/46">an issue</a>.</p>
<aside>
<h3>Update</h3><p>The issue has been fixed and the polyfill is now working! 🎉</p>
</aside>
<h2>Resources</h2>
<p>Find out more about Cascade Layers but reading this <a href="https://css-tricks.com/css-cascade-layers/">comprehensive guide</a> by <a href="https://twitter.com/TerribleMia">Miriam Suzanne</a>, or watch <a href="https://www.youtube.com/watch?v=zEPXyqj7pEA">Bramus’s video from CSS Day</a>.</p>
Quick Tip: Negative Animation Delay2022-07-28T00:00:00Zhttps://css-irl.info/quick-tip-negative-animation-delay/<p>Here’s a tiny CSS tip for making staggered animations feel waaaay more natural: Negative animation delay. I’ve seen this idea shared by master CSS animators <a href="https://twitter.com/jh3yy">Jhey</a> and <a href="https://twitter.com/amit_sheen">Amit</a> on separate occasions, and it’s such a neat little trick that it’s worth recording here!</p>
<p>Let’s say we have a group of elements, and we want them to animate in turn on a loop — such as this wavy text example. We’re calculating the <code>animation-delay</code> value them using a custom property corresponding to the element’s index.</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">--i</span><span class="token punctuation">:</span> 0</span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>B<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">--i</span><span class="token punctuation">:</span> 1</span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>O<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">--i</span><span class="token punctuation">:</span> 2</span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>U<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">--i</span><span class="token punctuation">:</span> 3</span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>N<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">--i</span><span class="token punctuation">:</span> 4</span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>C<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">--i</span><span class="token punctuation">:</span> 5</span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>I<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">--i</span><span class="token punctuation">:</span> 6</span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>N<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">--i</span><span class="token punctuation">:</span> 7</span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>G<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></code></pre>
<pre class="language-css"><code class="language-css"><span class="token selector">span</span> <span class="token punctuation">{</span>
<span class="token property">--delay</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--i<span class="token punctuation">)</span> * 200ms<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">animation</span><span class="token punctuation">:</span> bounce 500ms <span class="token function">var</span><span class="token punctuation">(</span>--delay<span class="token punctuation">,</span> 0<span class="token punctuation">)</span> infinite<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Here I’m setting that in the HTML, but we could use <a href="https://splitting.js.org/">Splitting.js</a>, a library that handles assigning custom properties for us (which enables a whole lot of cool text effects and more — <a href="https://css-irl.info/variable-font-animation-with-css-and-splitting-js/">I've written about it on this blog before</a>).</p>
<aside>If you’re doing this kind of animation with text, which involves wrapping each letter in a <code>span</code>, you’ll need to consider <a href="https://css-irl.info/quick-tip-negative-animation-delay/how-to-accessibly-split-text">how to make sure it’s accessible</a>.</aside>
<p>Here’s the demo with a regular, positive animation delay. You’ll notice that on the first iteration of the animation, each letter begins from a standing start.</p>
<figure>
<img src="https://css-irl.info/quick-tip-negative-animation-delay.webp" alt="The word “bouncing” animated letter by letter" width="758" height="383" />
<figcaption>With a positive delay, the looping animation begins with the first character. The last characters aren’t animated yet.</figcaption>
</figure>
<p>Now, if we instead calculate a negative delay (using -200ms instead of 200ms), we’ll see the animation behave as if it’s already in progress — we won’t be waiting for the last element to finally animate once <strong>all</strong> the others have done so. Lovely!</p>
<pre class="language-css"><code class="language-css"><span class="token selector">span</span> <span class="token punctuation">{</span>
<span class="token property">--delay</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--i<span class="token punctuation">)</span> * -200ms<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">animation</span><span class="token punctuation">:</span> bounce 500ms <span class="token function">var</span><span class="token punctuation">(</span>--delay<span class="token punctuation">,</span> 0<span class="token punctuation">)</span> infinite<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<figure>
<img src="https://css-irl.info/quick-tip-negative-animation-delay-02.webp" alt="The word “bouncing” animated letter by letter" width="758" height="383" />
<figcaption>With a negative delay, all the characters are animating straight away.</figcaption>
</figure>
<p>Try commenting out the <code>--stagger</code> custom property in this demo to see the difference.</p>
<p class="codepen" data-height="483" data-default-tab="result" data-slug-hash="dymZMgo" data-user="michellebarker" style="height: 483px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/dymZMgo">
Bouncing letters</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
Tech Workers and Climate Action2022-07-25T00:00:00Zhttps://css-irl.info/tech-workers-and-climate-action/<p><a href="https://twitter.com/davatron5000">Dave Rupert</a> recently published <a href="https://daverupert.com/2022/07/where-i-m-at-on-climate-change/">a post expressing his frustration with the urgent case for action on climate change</a>, and lack of co-ordinated policy action by the people in power. While much of the world suffers from extreme high temperures this summer, climate change has become a visible, tangible issue for many of us.</p>
<p>Dave rightly points out that the environmental responsibility has been unfairly hoisted onto the consumer, when in reality this is by the design of governments and large corporations, who have vested interests in maintaining the status quo. Individual lifestyle changes, such as reusable shopping bags, sorting our recycling, or going vegan are never going to make much of a difference when faced with the unstoppable juggernaut of capitalism.</p>
<p>I agree that widespread societal change shouldn’t depend on persuading 7 billion individuals to make daily sustainable choices, when many can’t or won’t. But as comparatively priviledged tech workers, we shouldn’t underestimate our power and influence. <a href="https://css-irl.info/building-a-greener-web/">As I’ve written before</a>, many of us are in a position to push for change within larger organisations, and extend the reach of our actions far beyond ourselves. We have the choice to take the lead set a positive example.</p>
<p>I believe individual action matters. Without it, it’s all too easy for companies and governments to dismiss environmental concerns by claiming there is no appetite for change. But ultimately, real change will only happen when we band together and demand it. One of the first steps we can take is to educate ourselves and others. <a href="https://twitter.com/hanopcan">Hannah Smith</a> recently shared a <a href="https://twitter.com/hanopcan/status/1549654342382354437?s=20&t=1YUJjd_6plvs3-RNG4Ooyg">Twitter thread</a> of climate action resources for the tech industry. The <a href="https://rebellion.global/">Extinction Rebellion</a> site has plenty of detail on the climate crisis we are facing, with links to resources.</p>
<p>Let’s be honest, many of the companies we’re highly familiar with in the tech industry are huge contributors to climate change, whether through their own actions (or inaction), their <a href="https://www.theguardian.com/environment/2021/sep/20/big-tech-climate-change">lobbying activities</a>, <a href="https://www.theverge.com/2022/5/18/23124241/big-tech-financing-fossil-fuels-cash-investments-climate-change-emissions">financial structures</a> or the industries they serve. We should be demanding better of them.</p>
<p><a href="https://daverupert.com/2022/07/where-i-m-at-on-climate-change/">Read Dave’s article →</a></p>
Logical Properties for Useful Shorthands2022-07-19T00:00:00Zhttps://css-irl.info/logical-properties-for-useful-shorthands/<p><strong>This article was updated on 28 July 2022 to include the section covering older browsers.</strong></p>
<p>Something I like about logical properties is the ability to set margins or padding on just a single axis on an element, while leaving the other alone. Say we have an element on which we’ve set some padding using the <code>padding</code> shorthand:</p>
<pre class="language-css"><code class="language-css"><span class="token comment">/* This gives us 1rem padding all the way around */</span>
<span class="token selector">.box</span> <span class="token punctuation">{</span>
<span class="token property">padding</span><span class="token punctuation">:</span> 1rem<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>If, later on, we want to adjust the padding <strong>only</strong> on the x-axis (while preserving our original value on the y-axis), we have a couple of choices:</p>
<ol>
<li>We could use <code>padding-left</code> and <code>padding-right</code>. Fine, but longer than our original shorthand.</li>
</ol>
<pre class="language-css"><code class="language-css"><span class="token selector">.box--some-variant</span> <span class="token punctuation">{</span>
<span class="token property">padding-right</span><span class="token punctuation">:</span> 2rem<span class="token punctuation">;</span>
<span class="token property">padding-left</span><span class="token punctuation">:</span> 2rem<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<ol start="2">
<li>We could use the shorthand, but remembering to include our original values.</li>
</ol>
<pre class="language-css"><code class="language-css"><span class="token selector">.box--some-variant</span> <span class="token punctuation">{</span>
<span class="token property">padding</span><span class="token punctuation">:</span> 1rem 2rem<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>I’m not a huge fan of having to repeat the original value for the y-axis padding. We <strong>could</strong> abstract those values out into custom properties — something like this:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.box</span> <span class="token punctuation">{</span>
<span class="token property">padding</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--py<span class="token punctuation">,</span> 1rem<span class="token punctuation">)</span> <span class="token function">var</span><span class="token punctuation">(</span>--px<span class="token punctuation">,</span> 1rem<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.box--some-variant</span> <span class="token punctuation">{</span>
<span class="token property">--px</span><span class="token punctuation">:</span> 2rem<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>On the other hand, there is a <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Logical_Properties">logical property</a> that enables us to adjust the padding on a single axis: <code>padding-inline</code> refers to the padding on the x-axis when the document is in the left-to-right (default) or right-to-left <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/writing-mode">writing mode</a>. <code>padding-block</code> refers to the y-axis. (Those directions are reversed for a vertical writing mode.)</p>
<p>So instead we could write:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.box</span> <span class="token punctuation">{</span>
<span class="token property">padding</span><span class="token punctuation">:</span> 1rem<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.box--some-variant</span> <span class="token punctuation">{</span>
<span class="token property">padding-inline</span><span class="token punctuation">:</span> 2rem<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p><a href="https://codepen.io/michellebarker/pen/RwMpBRO">Codepen demo →</a></p>
<p>The same applies to margins, borders and a bunch of others. And check out the <a href="https://css-irl.info/a-utility-class-for-covering-elements/"><code>inset</code></a> property for a great positioning shorthand!</p>
<p>Logical properties are <a href="https://caniuse.com/?search=logical%20properties">very well supported</a> in browsers now, so it’s a good time to start using them.</p>
<h2>Update</h2>
<p>While logical property support is very good in modern browsers, as <a href="https://twitter.com/simevidas/status/1549441864255807490?s=20&t=zaJb-OTH8_sLNzPSPuiw9Q">Šime pointed out</a>, it’s a good idea to polyfill or provide fallbacks for older browsers, otherwise users of older browsers will experience layout bugs. If you use PostCSS then the <a href="https://github.com/csstools/postcss-logical">PostCSS logical properties plugin</a> should have you covered, otherwise you should consider testing for support using a <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Conditional_Rules/Using_Feature_Queries">feature query</a>.</p>
Masonry? In CSS?!2022-07-05T00:00:00Zhttps://css-irl.info/masonry-in-css/<p>I spoke about CSS layout at <a href="https://cssday.nl/2022">CSS Day conference</a> recently, and in the Q&A session afterwards I was asked about masonry layout in CSS. Masonry layout, in case you’re not aware, is where different height items are laid out in columns but, rather than being aligned on the row axis, the items below effectively move up to plug any gaps, kind of like a bricklayer — hence the name. Pinterest’s grid design is a commonly-cited example whenever the masonry conversation comes up.</p>
<figure>
<img src="https://css-irl.info/masonry-in-css-01.webp" alt="Masonry-style grid of rectangles of differing proportions" width="1600" height="817" />
</figure>
<h2>The current state of masonry in CSS</h2>
<p>Currently we can build a kind of faux-masonry (or “fakesonry”, if you will) layout in CSS by using columns.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.list</span> <span class="token punctuation">{</span>
<span class="token property">columns</span><span class="token punctuation">:</span> 20rem<span class="token punctuation">;</span>
<span class="token property">column-gap</span><span class="token punctuation">:</span> 1rem<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p class="codepen" data-height="440" data-default-tab="result" data-slug-hash="RwMWYpb" data-user="michellebarker" style="height: 440px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/RwMWYpb">
Masonry grid (Firefox with flag enabled)</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>This will create a layout that <strong>looks</strong> like masonry, but the items are ordered by column, not by row. If you were using this layout for an entire page, for the user to browse the items in order they would need to work their way down each column, right to the end before scrolling back up to the top of the page again. Perhaps this might be an OK visual experience if the order doesn’t matter, but users navigating via a keyboard or screenreader would still have to browse by column. When we think of a masonry layout, we normally expect that items are ordered horizontally (or along the inline axis).</p>
<p>We can also build a grid that kind of looks like masonry, but it would require manual placement of grid items, and the height of each item wouldn’t truly be determined by the content. In fact, there are a number of ways we can kind of, <strong>almost</strong> do masonry in CSS, and some of them might serve us well in some situations — check out <a href="https://css-tricks.com/piecing-together-approaches-for-a-css-masonry-layout/">this CSS Tricks article from 2019</a> that collates a few of them. (Beware of re-ordering with CSS though. It’s not recommended for accessibility reasons.)</p>
<p>But building a <strong>true</strong> masonry layout currently requires Javascript, such as the <a href="https://masonry.desandro.com/">Masonry</a> library created for precisely this purpose.</p>
<h2>What’s next for masonry?</h2>
<p>I’d remember masonry in CSS being discussed by Rachel Andrew some time ago, but I admit it’s kind of fallen off my radar recently. I think that’s partly due to the fact that I’m just not required to build that many masonry layouts these days. Some of that might be down to changing jobs, but I also suspect that masonry just isn’t as popular among designers as it once was. To me, it feels like a very late-2010s design trend, from when Pinterest was all the rage.</p>
<p>Perhaps it’s also down to greater performance and accessibility awareness. Using a JS library for layout isn’t going to do your performance metrics any favours, and is something I’d advise against where possible. And sure, masonry looks cool* (*or it did in 2017), but I’d argue it’s not the most user-friendly layout: the order of the content isn’t as clear when items are not aligned. (I don’t have any evidence to back that up, it’s just a personal feeling.)</p>
<h2>But what about masonry in CSS?</h2>
<p>Ah yes, the whole point of this article. Despite masonry arguably being less popular, it would still be great to have a way to do it without JS. Well, the <a href="https://drafts.csswg.org/css-grid-3/#masonry-layout">CSS Grid Level 3 Specification</a> includes a proposal for masonry layout. In fact, it can already be enabled with a flag in Firefox, so you can try it for yourself.</p>
<p>It works be using the keyword <code>masonry</code> for the <code>grid-template-columns</code> or <code>grid-template-rows</code> property. This example will give us a 6-column grid with masonry applied to the row axis, to replicate the Pinterest-style grid:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.grid</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token property">grid-template-rows</span><span class="token punctuation">:</span> masonry<span class="token punctuation">;</span>
<span class="token property">grid-template-columns</span><span class="token punctuation">:</span> <span class="token function">repeat</span><span class="token punctuation">(</span>6<span class="token punctuation">,</span> 1fr<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p class="codepen" data-height="431" data-default-tab="result" data-slug-hash="QWmjBXE" data-user="michellebarker" style="height: 431px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/QWmjBXE">
Masonry grid (Firefox with flag enabled)</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>For a full overview, Rachel has a <a href="https://www.smashingmagazine.com/native-css-masonry-layout-css-grid/">great write up from 2020</a> in Smashing Magazine.</p>
<h2>Does masonry belong in Grid?</h2>
<p>A while back, there was some debate about whether masonry belongs in the Grid specification, as it’s not really Grid <strong>or</strong> flexbox, but has elements of both. Or perhaps it’s own thing altogether? While I did have some reservations about it being part of the Grid spec, I think the current implementation makes sense. It feels a little like <a href="https://www.w3.org/TR/css-grid-2/#subgrids">Subgrid</a> in some respects, where we’re effectively telling an element to use a <strong>different</strong> grid on either the row or column axis — albeit one determined not by its parent, but by a predetermined algorithm.</p>
<p>Another, more soul-searchy, question is whether the effort is warranted. If masonry is a declining design trend, the sometimes glacial pace of web standards might ensure that by the time browser support is widespread, no one really wants to use it anyway.</p>
<p>While I personally would love to see masonry implemented, there are plenty of features that should arguably be a higher priority. Watch <a href="https://www.youtube.com/watch?v=w_gOOW2ARMk">Rachel’s talk from CSS Day</a> for details on the CSS features browsers are prioritising and working together to implement.</p>
Breaking Out of a Central Wrapper2022-07-01T00:00:00Zhttps://css-irl.info/breaking-out-of-a-central-wrapper/<p>This is an old trick, but one I reach for <strong>just</strong> often enough to warrant a post on this blog — for no reason other than my own reference!</p>
<h2>The problem</h2>
<p>Suppose you have a page of content, all neatly constrained within a central wrapper. Probably using some kind of utility class, like so:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.wrapper</span> <span class="token punctuation">{</span>
<span class="token property">width</span><span class="token punctuation">:</span> 70rem<span class="token punctuation">;</span>
<span class="token property">max-width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>
<span class="token property">margin-inline</span><span class="token punctuation">:</span> auto<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>(I’m using the <code>margin-inline</code> <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/margin-inline">logical property</a> as shorthand for <code>margin-left</code> and <code>margin-right</code>.)</p>
<figure>
<img src="https://css-irl.info/breaking-out-of-a-central-wrapper-01.webp" alt="Viewport with centered content in a wrapper element, denoted by a pink dotted line" width="1626" height="1238" />
</figure>
<p>But oh no! Suddenly you find yourself in need of a component that spans the full width of the page <strong>in the midst</strong> of your neatly constrained content. What to do? Well, there are a bunch of options.</p>
<figure>
<img src="https://css-irl.info/breaking-out-of-a-central-wrapper-02.webp" alt="Viewport with centered content with one item full width" width="1626" height="1238" />
</figure>
<h2>The solution(s)</h2>
<h3>Solution 1: Wrap the content either side</h3>
<p>OK, we <strong>could</strong> wrap the content above and below our full-width section in our <code>wrapper</code> class.</p>
<figure>
<img src="https://css-irl.info/breaking-out-of-a-central-wrapper-03.webp" alt="Content above and below the full-width section sits within individual wrapper elements, denoted by a pink dotted line" width="1626" height="1238" />
</figure>
<p>Sure, that’ll work well enough. But that relies on us having sufficient control over the markup, which is not always the case if, for instance, your content is coming from a CMS. And it doesn’t feel like the most flexible solution, if we want to add more full-width sections in the future.</p>
<h3>Solution 2: Wrap every component</h3>
<p>Instead of wrapping groups of components, what about if we wrap each one individually?</p>
<figure>
<img src="https://css-irl.info/breaking-out-of-a-central-wrapper-04.webp" alt="Every component either side of the full-width section has its own wrapper, denoted by a pink dotted line" width="1626" height="1238" />
</figure>
<p>Again, it works where we can control the markup, if we’ve built our own custom components. But in WordPress for instance, paragraphs, heading, list blocks etc. are rendered as simple HTML elements, without any kind of wrapping markup to hook into. There’s <strong>probably</strong> a way you could wrap every component in a <code><div></code> if you’re <span lang="fr">au fait</span> with PHP, WordPress and the block editor, but it’s certainly beyond my abilities. And, in any case, it might result in in some issues with accessibility and semantics, as well as just feeling kind of gross. Div soup anyone?</p>
<h3>Solution 3: Append a class to every component</h3>
<p>Rather than wrapping each component, how about adding a class? We’ll give each of the components that needs to be constrained the <code>wrapper</code> class. So our markup would end up something like this:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>main</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>wrapper<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Heading<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>wrapper<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
consequat.
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>wrapper<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore
eu fugiat nulla pariatur.
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span>
<span class="token comment"><!--Full width component--></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>wrapper<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia
deserunt mollit anim id est laborum.
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>main</span><span class="token punctuation">></span></span></code></pre>
<p>Or, we could go the other way: Set our wrapper styles on all direct children of our <code>main</code> element, then set a <code>full-width</code> class on the full-width ones.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">main > *</span> <span class="token punctuation">{</span>
<span class="token property">width</span><span class="token punctuation">:</span> 70rem<span class="token punctuation">;</span>
<span class="token property">max-width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>
<span class="token property">margin-inline</span><span class="token punctuation">:</span> auto<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">main > .full-width</span> <span class="token punctuation">{</span>
<span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>This doesn’t seem like a great solution, as it assumes each direct child needs to be the same width. You might have some components that need to be narrower than that central wrapper, but still align with the rest of the content.</p>
<figure>
<img src="https://css-irl.info/breaking-out-of-a-central-wrapper-07.webp" alt="Showing one component narrower than the others, but still aligned to wrapper" width="1626" height="1238" />
</figure>
<p>Adjusting the margins or widths of those elements could have unpredictable results. For example, setting a narrower width results in misalignment.</p>
<figure>
<img src="https://css-irl.info/breaking-out-of-a-central-wrapper-08.webp" alt="Paragraph is misaligned when its width is narrower" width="1626" height="1238" />
<figcaption>The paragraph is centrally aligned, rather than left-aligning to the wrapper as we want.</figcaption>
</figure>
<p>Not to mention, we’ll need to increase the specificity of the elements we want to target for individual styling.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">main > *</span> <span class="token punctuation">{</span>
<span class="token property">width</span><span class="token punctuation">:</span> 70rem<span class="token punctuation">;</span>
<span class="token property">max-width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>
<span class="token property">margin-inline</span><span class="token punctuation">:</span> auto<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">/* This will have no effect :( */</span>
<span class="token selector">p</span> <span class="token punctuation">{</span>
<span class="token property">width</span><span class="token punctuation">:</span> 65ch<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">/* We gotta do this instead */</span>
<span class="token selector">main > p</span> <span class="token punctuation">{</span>
<span class="token property">width</span><span class="token punctuation">:</span> 65ch<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h3>Solution 4: Viewport width breakout technique</h3>
<p>Using viewport units, we can force an element to break out of the wrapper, without changing our original markup! Originally shared by <a href="https://twitter.com/una/status/951519740840873984?s=21">Una Kravets</a> in 2018, this little CSS snippet will do just that:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.full-width</span> <span class="token punctuation">{</span>
<span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span>
<span class="token property">right</span><span class="token punctuation">:</span> 50%<span class="token punctuation">;</span>
<span class="token property">left</span><span class="token punctuation">:</span> 50%<span class="token punctuation">;</span>
<span class="token property">margin-left</span><span class="token punctuation">:</span> -50vw<span class="token punctuation">;</span>
<span class="token property">margin-right</span><span class="token punctuation">:</span> -50vw<span class="token punctuation">;</span>
<span class="token property">max-width</span><span class="token punctuation">:</span> 100vw<span class="token punctuation">;</span>
<span class="token property">width</span><span class="token punctuation">:</span> 100vw<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<figure>
<img src="https://css-irl.info/breaking-out-of-a-central-wrapper-05.webp" alt="Single central wrapper (denoted by pink dotted line), but showing a class applied to the full-width section" width="1626" height="1238" />
</figure>
<p>Handy! But you know what? We can do it even more concisely with a transform:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.full-width</span> <span class="token punctuation">{</span>
<span class="token property">width</span><span class="token punctuation">:</span> 100vw<span class="token punctuation">;</span>
<span class="token property">margin-left</span><span class="token punctuation">:</span> 50%<span class="token punctuation">;</span>
<span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">translate3d</span><span class="token punctuation">(</span>-50%<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 0<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>This comes with a small caveat: It <strong>won’t</strong> work if we have <code>overflow: hidden</code> on the wrapper element. Still, if we can avoid that, and if we don’t have control over the markup (or even if we just need a quick fix), this is a great solution.</p>
<h3>Solution 5: Grid</h3>
<p>This is a grid pattern I reach for time and time again, ostensibly for precisely this sort of problem. We create a grid with two flexible-width outer columns (using the <code>fr</code> unit) and one or more max-width contrained columns. In this case, a single central column will serve our needs. I like to name my grid lines, to make placement easier:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.wrapper</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token property">grid-template-columns</span><span class="token punctuation">:</span>
[full-start] 1fr [wrapper-start]
<span class="token function">minmax</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 70rem<span class="token punctuation">)</span> [wrapper-end] 1fr [full-end]<span class="token punctuation">;</span>
<span class="token comment">/* Optional gap */</span>
<span class="token property">column-gap</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--pad<span class="token punctuation">,</span> 1rem<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Then we can place all direct children of our wrapper into the central column, except the ones that need to break out:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.wrapper > *</span> <span class="token punctuation">{</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> wrapper<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.wrapper > .full-width</span> <span class="token punctuation">{</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> full<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<figure>
<img src="https://css-irl.info/breaking-out-of-a-central-wrapper-06.webp" alt="Items placed on a grid" width="1626" height="1238" />
</figure>
<p>I like to add a column gap when I’m using this technique, because it means our grid will be responsive straight off — when the viewport is narrow there will be a space between the edge of the viewport and the content in the central column, we don’t need to use padding. I prefer not to set a row gap, as I often need to have more control over the space between items, giving headings more space above them than paragraphs, for instance. Grid doesn’t permit us to set different gap values, so the spaces between every item would be the same. Opting for margins instead allows for more control.</p>
<p>This demo shows the technique used for a page layout.</p>
<p class="codepen" data-height="300" data-default-tab="result" data-slug-hash="gOepPJG" data-user="michellebarker" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/gOepPJG">
Untitled</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>This is my favourite solution to the breakout, and one I’ve used on plenty of sites. My only word of caution is that if you’re retro-fitting an old site with this technique, make sure you do plenty of testing to make sure any leftover workarounds in your code don’t cause layout bugs.</p>
CSS Day 20222022-06-14T00:00:00Zhttps://css-irl.info/css-day-2022/<figure>
<img srcset="https://css-irl.info/css-day-2022_1200.webp 1200w, https://css-irl.info/css-day-2022_900.webp 900w" sizes="(min-width: 1200px) 1200px, 90vw" src="https://css-irl.info/css-day-2022_1200.webp" width="1200" height="738" alt="View of a canal on a sunny day, with boats in the foreground, and buildings and trees stretching along the far bank" />
</figure>
<p>Last week I spoke about CSS layout at <a href="https://cssday.nl/2022">CSS Day</a> in Amsterdam. It was my first time attending the conference (which, this year, was actually two days), but I’m quite sure it won’t be the last. The incredible mix of speakers and attendees with an overwhelming passion for CSS blew me away, and every talk demonstrated in-depth knowledge in the speaker’s chosen area.</p>
<p>With so many insightful talks it’s hard to choose a favourite, but some highlights include <a href="https://twitter.com/bramus">Bramus</a>’s deep dive into the cascade (and <a href="https://developer.chrome.com/blog/cascade-layers/">Cascade Layers</a>), <a href="https://twitter.com/amit_sheen">Amit</a>’s guide to creating keyframe animations and Rachel’s insights into how browsers are working together to ship new CSS features interoperably, with <a href="https://web.dev/interop-2022/">Interop 2022</a>.</p>
<p>On Day 1, <a href="https://twitter.com/LeaVerou">Lea</a> gave an incredible presentation (on CSS variables) that blew everyone’s minds with what CSS can be capable of, now and in the future. <a href="https://twitter.com/theanaferreira">Ana</a> got many people thinking about the human side of remote work, and how employers can help empower their teams to work better, not harder.</p>
<p>Kicking off Day 2, <a href="https://twitter.com/argyleink">Adam</a>’s high-energy presentation on <a href="https://web.dev/css-scroll-snap/">Scroll Snap</a> showed how far the specification has come since I spent time tinkering around with it a couple of years ago. The Chrome dev tools for inspecting scroll containers look great, and I’m excited to try them out. Adam highlighted a bunch of cool use cases, tips and tricks, including building his entire presentation using Scroll Snap! You can find some examples in <a href="https://snap-gallery.netlify.app/">this useful collection</a> he built.</p>
<p>Later on, <a href="https://twitter.com/ivorjetski">Ben</a>’s part-live-coded talk provided a welcome change of pace from the information-overload many of us were feeling by then, as we were all invited to sit back, relax, and watch the process of drawing in CSS. If you haven’t come across his work before, check out his <a href="https://codepen.io/ivorjetski">Codepen profile</a> full of amazing examples of painstakingly-detailed CSS drawings.</p>
<p>The subject I was least familiar with was scoping, and <a href="https://twitter.com/tabatkins">Tab</a> demonstrated a number of ways to scope styles and use the shadow DOM. This is something I’ll definitely need to did into in more depth! Both Tab and <a href="https://twitter.com/hj_chen">Hui-Jing</a> did a great job MCing and relaying questions from the audience after each session. I love Q&A sessions when they’re well run, as they often allow the audience to hear speakers’ honest thoughts and feelings in a way that doesn’t sound too pre-prepared. The questions following my own talk certainly got me thinking about ways that developers can better communicate the capabilities of the modern web to designers, and how we can work together to build more resilient UIs.</p>
<p>There was something to take away from every session — I feel bad not mentioning each of them individually here, as without question, every one was exceptional! If you couldn’t be there I can definitely recommend watching the videos when they’re released, if you have the chance. You can find links to slides and resources for each of the talks on the <a href="https://cssday.nl/2022/speakers">speaker page</a>.</p>
<p>Those of us who were still in town the following Saturday were treated to a bonus event, in the form of a special pop-up edition of <a href="https://www.meetup.com/CSS-Cafe/">CSS Café</a>. I managed to catch two of the talks: the first by Amit, this time on 3D transforms, where he showed us how to create the effects of a camera in CSS. It was great to glimpse behind the curtain, into how his awe-inspiring CSS creations are formed! The other talk I saw was by <a href="https://twitter.com/eladsc">Elad Shechter</a>, on responsive design best practices. It was refreshing to see someone not just blindly following what we consider “best practices”, but question and improve upon them to reflect the reality of working on a project. It certainly gave me a few things to think about. You can find a version of Elad’s talk on <a href="https://eladsc.dev/2019/08/03/responsive-design-best-practices-english/">his website</a>.</p>
<p>While CSS Day is not the cheapest conference, it’s probably the best I’ve ever been to in terms of speaker quality, organisation and atmosphere, clearly planned with a lot of love and care. Everyone I spoke to came away inspired and with a renewed enthusiasm for CSS. I’ll be eagerly looking forward to next year.</p>
Writing Useful Alt Text2022-05-30T00:00:00Zhttps://css-irl.info/writing-useful-alt-text/<p>I enjoyed this article by Jeremy Keith on <a href="https://adactio.com/journal/19106">writing alt text for images</a>. In case you’re not aware of what “alt text” or (“alternative” text) is, it’s the text value of the <code>alt</code> attribute of an <code><img></code>. It <strong>should</strong> describe the image (although in practice doesn’t always!).</p>
<p>As a user you might never notice alt text, but there are contexts where it’s incredibly important. Screenreaders will announce the <code>alt</code> attribute to a user, so for someone who is blind or partially sighted, the <code>alt</code> attribute can provide vital content that might otherwise be missed. It’s not good practice to put important text content in an image, but if for some reason you do have to do it then providing the same information in the alt text is essential to ensuring screenreader users get an equivalent experience.</p>
<p>Another time alt text becomes useful is if an image fails to load. This sometimes happens in an email client, or maybe there’s a bad connection. Or maybe the image just takes a really long time to download. In these cases the alt text will be displayed to the user as a placeholder.</p>
<figure>
<img src="https://css-irl.info/writing-useful-alt-text-01.webp" alt="Pink box with the text “Two blue boxes with short text, one purple box with longer text”" width="835" height="438" />
<figcaption>How an image’s alt text might be displayed to a user if the image hasn’t loaded</figcaption>
</figure>
<p>As Jeremy points out, writing alt text isn’t always straightforward, and often requires thought, consideration and empathy. That’s why I have a particular loathing for CMSs that default to using the file name as alt text. Imagine, as a screenreader user, being read out file names such as “Screenshot 2022-05-22 at 14-43-16.png”. Unless all content authors are well-trained, this practice is likely to result in some very inaccessible alt text, where in fact, even an empty <code>alt</code> attribute would be preferable.</p>
<h2>Empty or missing alt attributes</h2>
<p>An empty <code>alt</code> attribute isn’t necessarily a bad thing: Images with <code>alt=""</code> will be interpreted by screenreaders as <a href="https://www.w3.org/WAI/tutorials/images/decorative/">purely decorative</a> and therefore ignored, which is entirely appropriate for, say, an image uses as a decorative background. It can also be a good idea to use an empty <code>alt</code> attribute when the image is already described by the surrounding text. (The <a href="https://www.w3.org/WAI/tutorials/images/decorative/">W3C page on decorative images</a> includes some useful examples.) But it shouldn’t be used as a lazy shortcut to skip the work of writing helpful alt text.</p>
<p>There’s an important difference between an empty alt attribute and one that’s missing entirely. A screenreader coming across an image with a missing alt attribute will announce the file name instead, which, as we've seen, is usually unhelpful.</p>
<h2>Writing alt text for text-heavy images</h2>
<p>An issue I’ve seen come up increasingly frequently is writing alt text for images that contain a lot of text. It’s become common to see people post screenshots of text on Twitter: displaying interactions when you don’t want to amplify the original author by retweeting or quote-tweeting, or posting a long conversation that has occurred via another medium are a couple of use cases.</p>
<p>Another case (popular in the developer community) is sharing code examples. Sites like <a href="https://carbon.now.sh/">Carbon</a> allow you to “prettify” your code examples and save them as images to share on social media. Without alt text these code examples are meaningless to users without the means to physically see the images.</p>
<p>Happily, Twitter now allows users to provide alt text for images, so in many cases including the code as text should suffice. But for longer text, what should we do? One possibility is to provide a link to the original content in the tweet itself. In the case of code, or even long-form content when it can’t be accessed elsewhere, it could be a <a href="https://docs.github.com/en/get-started/writing-on-github/editing-and-sharing-content-with-gists/creating-gists">Github Gist</a>, for example. (I’ve seen <a href="https://twitter.com/DavidDarnes">Dave Darnes</a> do this.)</p>
<p>I had a bit of an unusual case that gave me pause for thought recently when I posted a screenshot of an HTML webpage that had loaded without its CSS file. It contained a lot of text, but the point of posting the image was to show what that page looked like without its CSS, not the text itself.</p>
<figure>
<img src="https://css-irl.info/writing-useful-alt-text-02.webp" alt="Screenshot of tweet with the text “Another day working on the train where a CSS file fails to download, but THAT’S OK because ✨progressive enhancement✨”, accompanied a screenshot of the web page I was browsing at the time in its unstyled HTML form" width="598" height="409" />
</figure>
<p>I opted for alt text that described the content of the image, rather than copy-pasting the entire text:</p>
<blockquote>
<p>Screenshot of W3C CSS Color Module spec page showing table of contents</p>
</blockquote>
<p>My alt text was probably a little rushed and imperfect. I probably should have included the fact that the webpage was displayed in its pure HTML, unstyled form. But hopefully would have given enough information for someone to get the gist of the image, especially when accompanied by the <a href="https://twitter.com/MicheBarks/status/1530086988736516096">original tweet</a>. As Jeremy says in his article, the more you write it, the better you get.</p>
<p><a href="https://adactio.com/journal/19106">Read Jeremy’s article →</a></p>
Quick Tip: You Might Not Need Calc()2022-05-16T00:00:00Zhttps://css-irl.info/quick-tip-you-might-not-need-calc/<p>Did you know, if you use CSS math functions like <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/min"><code>min()</code></a>, <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/max"><code>max()</code></a> and <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/clamp"><code>clamp()</code></a> and you’re calculating any one of the arguments, you don’t need <code>calc()</code>? I think it was <a href="https://twitter.com/shadeed9">Ahmad Shadeed</a> who mentioned this on Twitter the other day, but I could be wrong.</p>
<p>So if you want to use <code>clamp()</code> for some fluid typography, say, with a <code>calc()</code> function for the middle value, instead of doing this:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">h1</span> <span class="token punctuation">{</span>
<span class="token property">font-size</span><span class="token punctuation">:</span> <span class="token function">clamp</span><span class="token punctuation">(</span>1.5rem<span class="token punctuation">,</span> <span class="token function">calc</span><span class="token punctuation">(</span>1rem + 3vw<span class="token punctuation">)</span><span class="token punctuation">,</span> 4rem<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>You can do this:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">h1</span> <span class="token punctuation">{</span>
<span class="token property">font-size</span><span class="token punctuation">:</span> <span class="token function">clamp</span><span class="token punctuation">(</span>1.5rem<span class="token punctuation">,</span> 1rem + 3vw<span class="token punctuation">,</span> 4rem<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>It totally makes sense, because <code>min()</code>, <code>max()</code> and <code>clamp()</code> <em>already are</em> math functions. But nonetheless, it’s something I wasn’t aware of before!</p>
<p>It works with custom properties too — check out <a href="https://codepen.io/michellebarker/pen/qBxRXmg">this demo</a>.</p>
<p>Incidentally, if you want to know more about fluid typography in CSS (including accessibility concerns), <a href="https://www.smashingmagazine.com/2022/01/modern-fluid-typography-css-clamp/">this comprehensive guide</a> by <a href="https://twitter.com/AdrianBeceDev">Adrian Bece</a> has you covered.</p>
My Browser Support Strategy2022-04-28T00:00:00Zhttps://css-irl.info/my-browser-support-strategy/<p>As I’ve <a href="https://css-irl.info/exciting-times-for-browsers-and-css/">written about recently</a>, it’s super exciting to see a bunch of new CSS features landing in browsers, and who can blame us for wanting to get started using them straight away?! It’s great to see browsers working to support new CSS features quicker than ever before, and that pace should persist with <a href="https://webkit.org/blog/12288/working-together-on-interop-2022/">Interop</a>, an initiative where browser vendors work together to implement features interoperably. And with so-called “evergreen” browsers, most users should see those new features right away, as their browsers update seamlessly.</p>
<p>But, as <a href="https://blog.jim-nielsen.com/2022/a-web-for-all/">Jim Neilsen pointed out</a>, that <strong>doesn’t</strong> mean every user has the latest and greatest browser version. Many older devices <strong>can’t</strong> update to the latest version. The OS doesn’t support it. This means that:</p>
<ol>
<li>You need to test your website on older devices, and</li>
<li>If you’re using fancy new CSS features, you need to provide fallbacks, or ensure that users whose devices don’t support those features can still get by without them.</li>
</ol>
<h2>Progressive enhancement</h2>
<p><a href="https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement">Progressive enhancement</a> is a web development strategy by which we ensure that the essential content and functionality of a website is accessible to as many users as possible, while providing an improved experience using newer features for users whose devices are capable of supporting them. Fun fact: while writing this article I opened the website <a href="https://www.whatismybrowser.com/guides/the-latest-version/safari">WhatIsMyBrowser.com</a> while on a train, with an intermittent internet connection. The CSS for the page wouldn’t load, but I still got the HTML and could use the page perfectly well, even if it wasn’t exactly pretty. That’s progressive enhancement!</p>
<p>Of course, in many cases users <strong>will</strong> be able to load your CSS file, but their browser might not support some of the features you choose to use. Sometimes that’s fine! Take the <code>aspect-ratio</code> property, which is relatively new, and wouldn’t be supported on, say, an iPhone 6, which is pegged to Safari version 12. If I set the <code>aspect-ratio</code> property on this element with a bit of text in, all that will happen in browsers that <strong>don’t</strong> support <code>aspect-ratio</code> is the element will be the height that it needs to be to accommodate the text. No big deal to the average user.</p>
<figure>
<img src="https://css-irl.info/my-browser-support-strategy-01.jpg" alt="Turquoise box with 3:2 aspect ratio" />
<figcaption>With aspect-ratio supported</figcaption>
</figure>
<figure>
<img src="https://css-irl.info/my-browser-support-strategy-02.jpg" alt="Turquoise box without aspect ratio applied" />
<figcaption>Without aspect-ratio supported</figcaption>
</figure>
<p>(<a href="https://codepen.io/michellebarker/pen/GRyVyZq">See the demo</a>)</p>
<h3>Fallbacks</h3>
<p>In other cases we might need to provide a fallback for a particular CSS feature. One way we can do that is by repeating the CSS property with a different value. Perhaps we want to use the new <code>lch()</code> color function, which is currently only supported in Safari. By including a <code>hsl()</code> value in the preceding rule, we can ensure than browsers that <strong>don’t</strong> support <code>lch()</code> have the <code>hsl()</code> value applied — the browser will simply ignore the value it doesn’t recognise.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.element</span> <span class="token punctuation">{</span>
<span class="token property">background</span><span class="token punctuation">:</span> <span class="token function">rgb</span><span class="token punctuation">(</span>84% 0% 77%<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">background</span><span class="token punctuation">:</span> <span class="token function">lch</span><span class="token punctuation">(</span>50% 100 331<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h3>Feature queries</h3>
<p>Sometimes the fallback needs to be a little more complex, in which case we could use a <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Conditional_Rules/Using_Feature_Queries">feature query</a>. Perhaps our aspect-ratio component has an image background.</p>
<figure>
<img src="https://css-irl.info/my-browser-support-strategy-03.jpg" alt="Aspect ratio box with a kitten image background" />
</figure>
<p>With a shallow height (where the browser doesn’t support <code>aspect-ratio</code>) the image might appear oddly cropped and hard to discern. Using a feature query, we could set a minimum height where <code>aspect-ratio</code> isn’t supported.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.aspect-box</span> <span class="token punctuation">{</span>
<span class="token property">min-height</span><span class="token punctuation">:</span> 10rem<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token atrule"><span class="token rule">@supports</span> <span class="token punctuation">(</span><span class="token property">aspect-ratio</span><span class="token punctuation">:</span> 3 / 2<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token selector">.aspect-box</span> <span class="token punctuation">{</span>
<span class="token property">min-height</span><span class="token punctuation">:</span> auto<span class="token punctuation">;</span>
<span class="token property">aspect-ratio</span><span class="token punctuation">:</span> 3 / 2<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>(<a href="https://codepen.io/michellebarker/pen/XWVvVpO">See the demo</a>.)</p>
<p>We could go even further, and use the <a href="https://css-tricks.com/aspect-ratio-boxes/">old-fashioned method</a> for setting an aspect ratio for browsers that don’t support the modern property. But remember, the goal of progressive enhancement is not to provide the <strong>same</strong> experience for users of older browsers, but to provide a <strong>useable</strong> experience. There is little point in writing more than double the amount of code to ensure an identical result. If it’s absolutely essential that virtually all users get the aspect ratio images, (and believe me, I’ve been in plenty of situations where it <strong>has</strong> been deemed essential — i.e. by key stakeholders using older devices!) then you’re free to code them the old-fashioned way. But, in my opinion, that should be a last resort.</p>
<h2>Polyfilling</h2>
<p>Thankfully the number of situations where we need to polyfill a feature is growing smaller by the day, but in a few cases where we want to use a new CSS feature, it might still be necessary. The <code>:focus-visible</code> pseudo-class (which we can use for applying accessible focus styles to an element that’s focused <a href="https://drafts.csswg.org/selectors-4/#the-focus-visible-pseudo">only when it’s useful to the user</a>) has only recently gained widespread browser support, and for a while was supported in some browsers but not others. There is currently no way to test for support of pseudo-classes using feature queries (which might allow us to provide a <code>:focus</code> style as a fallback for those browsers), using a polyfill <a href="https://github.com/WICG/focus-visible">like this one</a> is a useful alternative.</p>
<p>Once you are confident that you no longer need a polyfill (e.g. when a feature is widely supported) it’s a good idea to remove it, to avoid serving unnecessary JS to your users.</p>
<h2>Testing</h2>
<p>The other important piece of the equation is, of course, to test your websites! There is no substitute for real-device testing, so if you have access to a device lab you should absolutely use it. At the very least, aim to test on a few different devices if you can get hold of them. Issues with touch interaction, for example, will only become apparent if you’re using an actual touch-screen device.</p>
<h3>Testing platforms</h3>
<p>If you can’t test on real devices (and plenty of companies don’t have the budget to keep a well-stocked device lab), online testing platforms such as <a href="https://www.browserstack.com/">Browserstack</a> and <a href="https://www.lambdatest.com/">LamdaTest</a> enable us to test on a range of platforms and devices. Some of these platforms provide cheaper emulator options, but I would urge you to use the real-device option if you can, as they will help you catch issues that aren’t replicated in emulators. They may be more expensive, but can help you prevent more costly issues arising if a bunch of customers can’t access your site!</p>
<h3>Which browsers to test?</h3>
<p>With the thousands of different devices, operating systems and browser versions, it’s clearly impossible to test in every possible environment! So which ones should you test in? I find the <a href="https://gs.statcounter.com/browser-market-share">Statcounter</a> website to be very useful here, as it shows the most-used browsers over the course of a year. You can filter by year, date, platform and region: If you’re making a website for Trowbridge town council in the UK, for example, the UK stats are probably going to be far more valuable to you than the worldwide ones.</p>
<p>The site also lets you specifically check the stats for <a href="https://gs.statcounter.com/browser-version-market-share">browser versions</a>, which is what I find most useful for testing. As a rule of thumb, I aim to cover at least the past two years of the most popular listed browsers with my testing, plus a couple of older browsers/devices — particularly versions of Safari that may be running on older devices.</p>
<p>If you’re working for a client, and you’re in a position to do so, it might be worth coming up with a written agreement that sets out explicitly how far back you intend to test, and what they can expect from browsers that don’t support all the features (versus what constitutes a bug). Educating clients about progressive enhancement could save quite a lot of time and explanation down the line, when they’re reporting “bugs” that are in fact fallbacks for older browsers!</p>
<h3>Other ways to test</h3>
<p>In addition to cross-browser testing, there are some other methods we should consider when testing our websites:</p>
<ul>
<li><strong>Keyboard navigation</strong> – Many people use a keyboard instead of a mouse. Can you navigate the website using only your keyboard? Are focused elements apparent?</li>
<li><strong>Screenreaders</strong> – People with visual impairment might use a screenreader to browse the website, which reads the content of the page. MacOS has a built-in screenreader, <a href="https://www.apple.com/voiceover/info/guide/_1121.html">VoiceOver</a>, and Chromebooks include <a href="https://support.google.com/accessibility/answer/7031755?hl=en">Chrome Vox</a>. (I definitely need to get better at testing with screenreaders!)</li>
<li><strong>Zooming</strong> – Plenty of users (myself included!) increase the zoom level in their browsers. Try zooming to at least 15%. Does the font size increase as expected? Does the layout break?</li>
<li><strong>Reduced motion</strong> – Users can set a preference for reduced motion at OS level. Does your website <a href="https://www.smashingmagazine.com/2021/10/respecting-users-motion-preferences/">respect their motion preferences</a>? Can they pause animations that might be distracting, or worse, trigger cognitive issues?</li>
<li><strong>Accessibility tools</strong> – Tools like <a href="https://www.deque.com/axe/">Axe</a> scan you webpage and check for accessibility issues (such as missing alt text, colour contrast and semantic markup) that would be time-consuming to check manually. It’s worth running your site through one of these tools to catch issues that might otherwise go unnoticed.</li>
</ul>
<p>This list is by no means exhaustive, but should give you some things to think about when you’re next testing your website. Remember, by building with progressive enhancement, we can aim to reduce the number of issues raised by testing, and provide an experience that accommodates everyone.</p>
Animated Grid Tracks with :has()2022-04-05T00:00:00Zhttps://css-irl.info/animated-grid-tracks-with-has/<figure>
<img src="https://css-irl.info/animated-grid-tracks-with-has-02.jpg" alt="Grid of four segments with brightly coloured backgrounds and cupcake photo in the first section" />
<figcaption>Screenshot from a Codepen demo. Hovering on a grid item expands the grid tracks.</figcaption>
</figure>
<p>Somehow it’s already three months since I wrote about the CSS <code>:has()</code> pseudo-class <a href="https://css-irl.info/has-has-landed-in-safari/">landing in Safari Technology preview</a>. The latest Safari (proper) release has just shipped with support, and can be enabled with a flag in the next Chrome release.</p>
<p>Meanwhile, I’ve been playing around with some ideas on how we can use <code>:has()</code> for styling a gallery or product grid layout. One of the cool things about the selector is that it allows us to not only style a parent element based on any one of its children, but to also style that child’s siblings too. So, building a menu, we could apply some styles to other elements in the menu when one of the links is hovered:</p>
<pre class="language-css"><code class="language-css"><span class="token comment">/* Style the list border when link is hovered */</span>
<span class="token selector">ul:has(a:hover)</span> <span class="token punctuation">{</span>
<span class="token property">border</span><span class="token punctuation">:</span> 2px solid whitesmoke<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">/* Style any other links in the list when a link is hovered */</span>
<span class="token selector">ul:has(a:hover) a:not(:hover)</span> <span class="token punctuation">{</span>
<span class="token property">opacity</span><span class="token punctuation">:</span> 0.5<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<figure>
<img src="https://css-irl.info/animated-grid-tracks-with-has-01.jpg" alt="Menu bar with item hovered" />
<figcaption>Menu bar in its original state, and with item hovered</figcaption>
</figure>
<p>(<a href="https://codepen.io/michellebarker/pen/GRyQZWZ">See the demo</a>)</p>
<p>That extends nicely to a grid design, and combined with animated grid tracks could make for some exciting layout possibilities. What if, when the user hovers on a grid item, we could change the size of our grid tracks?</p>
<p>The ability to animate grid tracks (<code>grid-template-rows</code> and <code>grid-template-columns</code>) is actually written into the <a href="https://www.w3.org/TR/css-grid-1/">CSS Grid specification</a>, and has been supported in Firefox since 2019. But, sad to say, no other browsers currently support grid track animation. <a href="https://blog.bitsrc.io/animating-css-grid-rows-and-columns-4b3b0997d06a">Chen Hui-Jing wrote about it</a> at the time, and included a cool demo by Olivia Ng of an expanding menu bar:</p>
<p class="codepen" data-height="573" data-default-tab="result" data-slug-hash="LvKdRP" data-user="oliviale" style="height: 573px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/oliviale/pen/LvKdRP">
CSS Grid: In-flight Entertainment Screen</a> by Olivia Ng (<a href="https://codepen.io/oliviale">@oliviale</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>But perhaps one of the reasons why we didn’t see more people playing around with animated grid tracks at the time (aside from the limited browser support) was because one of the more obvious use cases is expanding a grid track when hovering on an item <strong>within</strong> the grid. Until <code>:has()</code> arrived, we didn’t have a way to style the grid based on the hover state of a child — we would have to use Javascript.</p>
<p>This demo combines animated grid tracks with the <code>:has()</code> selector. When you hover over a grid item, the item’s grid tracks expand to take up a greater proportion of the space, and the labels of the sibling items fade out. You’ll need to view it in Safari to see this in action — in other browsers you’ll still see a hover effect on the item you’re hovering, just not on the grid and the item’s siblings.</p>
<p class="codepen" data-height="536" data-default-tab="result" data-slug-hash="vYpdEgQ" data-user="michellebarker" style="height: 536px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/vYpdEgQ">
grid / :has + animated grid tracks (Safari only)</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<h2>Polyfilling</h2>
<p>Unfortunately, although <code>:has()</code> is supported in Safari, animated grid tracks are not. To get around this in the above demo, I’m using the <a href="https://greensock.com/">Greensock</a> library to animate custom properties for the grid tracks. It’s not perfect — to be honest, I’ve gone for a minimal-code outcome — but hopefully illustrates what’s possible while we cross our fingers for wider support!</p>
<aside>
<h3>Update</h3>
<p>Safari Technology Preview and Chrome Canary have now shipped support for animated grid tracks! Here’s a <a href="https://codepen.io/michellebarker/pen/vYRVbQX">revised demo</a>, with an improved fallback for browsers that don’t support <code>:has()</code>. (Detecting support for animated grid tracks is not easily possible.)</p>
</aside>
Exciting Times for Browsers (and CSS)2022-03-31T00:00:00Zhttps://css-irl.info/exciting-times-for-browsers-and-css/<p>Last month I wrote about some of the exciting <a href="https://www.smashingmagazine.com/2022/03/new-css-features-2022/">new CSS features</a> you can expect to see coming to a browser near you in 2022 for Smashing Magazine. It’s certainly an exciting time to be working on the web right now, with browser support for new features moving along at lightning-fast pace! This month saw Safari drop a <a href="https://webkit.org/blog/12445/new-webkit-features-in-safari-15-4/">new release</a> that includes support for a <em>tonne</em> of new stuff, including <code>:has()</code> (A.K.A. the “parent selector”), <code>accent-color</code>, and <a href="https://css-tricks.com/css-cascade-layers/">Cascade Layers</a> (all of which are covered in my Smashing article), as well as some additional gems:</p>
<h2>Trigonometric functions</h2>
<p>I almost missed this line when skim-reading the Safari release announcement, but yep, Webkit has delivered support for CSS <a href="https://www.w3.org/TR/css-values-4/#trig-funcs">trigonometric functions</a>! I’m unbelievably excited about the possibilities this will open up for creative CSS, some of which are covered in a <a href="https://tympanus.net/codrops/2021/06/01/trigonometry-in-css-and-javascript-introduction-to-trigonometry/">series on trigonometry in CSS and JS</a> that I wrote for Codrops last year.</p>
<h2>New viewport units</h2>
<p>Viewport units (<code>vh</code>, <code>vw</code>, <code>vmin</code> and <code>max</code>) are wonderful, except when they’re not. Have you ever used <code>vh</code> only to find that it doesn’t work as expected on mobile? That’s because, quite often, the viewport height isn’t fixed, and changes when the user scrolls and the device menu bar moves out of view. The <a href="https://www.w3.org/TR/css-values-4/#viewport-relative-lengths">new viewport units</a> are a solution to that problem, allowing us to specify largest, smallest or dynamic viewport units. Webkit also added support for logical property-style viewport units (<code>vi</code>, <code>vb</code>, and the dynamic variations), allowing us to specify the viewport unit in the inline or block dimension, as opposed to width and height.</p>
<h2>:focus-visible</h2>
<p>This release brings Safari up to date with other browsers in supporting the <code>:focus-visible</code> pseudo-class, which is super handy for providing accessible focus styles, reducing the need for polyfills. <a href="https://css-tricks.com/almanac/selectors/f/focus-visible/">This CSS Tricks post</a> explains what’s so useful about it.</p>
<h2>Cool font stuff</h2>
<p>I have to admit, this one was <strong>completely</strong> off my radar: <a href="https://www.w3.org/TR/css-fonts-4/#font-palette-prop"><code>font-palette</code></a> and <a href="https://www.w3.org/TR/css-fonts-4/#font-palette-values"><code>@font-palette-values</code></a> provide a way for developers to reference a pre-coloured font, and to define a font’s custom colour palette. I have yet to dig into this, but it looks like a pretty exciting step for web typography!</p>
<h2>scroll-behavior</h2>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-behavior"><code>scroll-behavior</code></a> allows us to specify the way in which users are scrolled to an element in a webpage when triggered by an action (say, by clicking on an in-page anchor link). Previously the page would jump to the specified point, but instead we can instruct it to scroll smoothly. Most browsers have supported this feature for a while now, but Safari has been holding out on us. Now we have can smoothly scroll everywhere — both with <code>scroll-behavior: smooth</code> property, and in JS with the <code>scrollTo</code> option:</p>
<pre class="language-js"><code class="language-js">window<span class="token punctuation">.</span><span class="token function">scrollTo</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
<span class="token literal-property property">top</span><span class="token operator">:</span> <span class="token number">1000</span><span class="token punctuation">,</span>
<span class="token literal-property property">left</span><span class="token operator">:</span> <span class="token number">0</span><span class="token punctuation">,</span>
<span class="token literal-property property">behavior</span><span class="token operator">:</span> <span class="token string">'smooth'</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<h2>HTML and Javascript</h2>
<p>In addition to some pretty awesome CSS features, we’ve also got some HTML and JS goodies. Just a few that stand out are:</p>
<h3>Lazyloading images</h3>
<p>The <code>loading="lazy"</code> attribute on the <code><img></code> element allows image loading to be deferred until scrolled into view, and is a great performance win. Again, Safari has lagged behind other browsers on this up until now.</p>
<h3><dialog> element</h3>
<p>The <code><dialog></code> enables us to create a modal with all the accessibility stuff baked right in, rather than having to faff about with workarounds. Hooray! <a href="https://twitter.com/una">Una</a> has a beautiful demo right <a href="https://codepen.io/una/pen/dyJRwvG">here</a>.</p>
<h3>Array.prototype.at()</h3>
<p>This cheeky little <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at">array method</a> lets us pass in a negative integer, to select an item from the <strong>end</strong> of the array. Say goodbye to the days of doing <code>someArray[someArray.length - 1]</code>.</p>
<h2>Interop 2022</h2>
<p>This month also saw a joint announcement from all the major browser engines, which have <a href="https://webkit.org/blog/12288/working-together-on-interop-2022/">committed to working together</a> to implement major new CSS features interoperably — meaning they are implemented in the same way in every browser. The list of priority features includes some highly desirable stuff, such as container queries, and should hopefully mean CSS feature development continues to gather pace! <a href="https://wpt.fyi/interop-2022">Visit the dashboard</a> to see how browsers are doing.</p>
<p>It’s pretty incredible to see Safari shoot way up the rankings for many of these features, and signals a clear commitment by the team to push things forward in a browser that has previously been widely accused of holding CSS back. Long may it continue!</p>
<p>Not to be outdone, Nicole Sullivan confirmed <a href="https://twitter.com/stubbornella/status/1508690609917206532?s=20&t=g0t1SpE0LVi2qr0gUtJ3HQ">via Twitter</a> that the Chrome team is working hard at all implementing pretty much all of the most commonly-requested features too. It’s an exciting time for CSS for sure.</p>
Building an Interactive Sparkline Graph with D32022-03-29T00:00:00Zhttps://css-irl.info/building-an-interactive-sparkline-graph-with-d3/<p>D3 is a great JavaScript library for building data visualizations using SVG elements. In this tutorial, learn how to build an interactive sparkline graph that takes inspiration from the NPM website. We’ll also learn how to use CSS custom properties to create multiple color schemes.</p>
<p><a href="https://tympanus.net/codrops/2022/03/29/building-an-interactive-sparkline-graph-with-d3/">Read the article</a></p>
The Web Doesn’t Have Version Numbers2022-03-19T00:00:00Zhttps://css-irl.info/the-web-doesnt-have-version-numbers/<p>You’ve probably heard the term “web3” bandied around quite a bit over the past year or so — along with related terminology like “blockchain”, “crypto” and “NFTs”. If you’re a web developer you’d be forgiven for thinking that this technology is something you need to jump on right away, or risk being left behind. <a href="https://hiddedevries.nl/en/blog/2022-01-03-the-web-doesnt-have-version-numbers">This post</a> by <a href="https://twitter.com/hdv">Hidde de Vries</a> explains why we should be cautious about the misleading term “web3”, and <strong>not</strong> mistake it for some kind of official release. As he writes:</p>
<blockquote>
<p>There is no institution that regularly releases new versions of the web, and recently happily announced this one.</p>
</blockquote>
<p>It certainly doesn’t hurt to be aware of these new technologies. They are the subject of much debate at the moment, and may or may not prove useful in the long run. But, Hidde argues, there is plenty of the web that is getting on just fine without it, and lots of exciting innovation happening on the web platform itself that have nothing to do with the blockchain.</p>
<p>I hope that “web3” and its associated terminologies don’t become buzzwords that recruiters look for in a CV. For most web development roles, specialist knowledge of this area is completely unnecessary.</p>
<p><a href="https://hiddedevries.nl/en/blog/2022-01-03-the-web-doesnt-have-version-numbers">Read the article</a></p>
A Reason to Self-Host Fonts2022-03-08T00:00:00Zhttps://css-irl.info/a-reason-to-self-host-fonts/<p>The other day I noticed a strange thing had happened with the title font on my personal site. Where once the glyphs were clearly defined by glowing outlines, suddenly the outlines were all over the place, bisecting the glyphs in odd ways.</p>
<figure>
<img src="https://css-irl.info/a-reason-to-self-host-fonts-01.jpg" alt="Screenshot of title font with weird outlines" />
<figcaption>After: Font rendering after the change</figcaption>
</figure>
<figure>
<img src="https://css-irl.info/a-reason-to-self-host-fonts-02.jpg" alt="Screenshot of title font with normal outlines" />
<figcaption>Before: How the font should look</figcaption>
</figure>
<p>My immediate assumption was that it was a browser compatibility issue. After all, I was using the non-standard CSS property <code>text-stroke</code>.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">h1</span> <span class="token punctuation">{</span>
<span class="token property">-webkit-text-stroke</span><span class="token punctuation">:</span> 2px <span class="token function">var</span><span class="token punctuation">(</span>--accent<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">text-stroke</span><span class="token punctuation">:</span> 2px <span class="token function">var</span><span class="token punctuation">(</span>--accent<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Although <code>text-stroke</code> is currently well-supported, it comes with <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-text-stroke">a warning</a> that there could be inconsistencies between browser implementations, and behaviour might change in the future. I assumed that Firefox had changed its implementation. But a subsequent check revealed the bug was occurring in <strong>all</strong> browsers.</p>
<p>Christian Sonne suggested <a href="https://twitter.com/ChristianSonne/status/1495160895403696132?s=20&t=wgdWLtXNWnV8wkDpNvqE9w">on Twitter</a> that is could be the font itself that changed and, sure enough, that turned out to be the case. On my personal site I was using Google Fonts via the embed code rather than self-hosting. That means that, yep, Google could change the font at any time and I wouldn’t know about it — something I hadn’t considered before! As Killian Valkhof replied <a href="https://twitter.com/kilianvalkhof/status/1495302493374451712?s=20&t=wgdWLtXNWnV8wkDpNvqE9w">on Twitter</a>:</p>
<blockquote>
<p>Did you switch to/have a variable font? This is common for those, so the strokes can more easily morphed.
Still, I’d expect browser to union/rasterize those before adding strokes. Hopefully soon!</p>
</blockquote>
<p>Sure enough, Google appear to be upgrading a lot of their fonts to variable fonts, so that explanation would make sense.</p>
<h2>Should you self-host fonts?</h2>
<p>There are many performance arguments for and against self-hosting fonts versus using a third-party CDN. <a href="https://www.tunetheweb.com/blog/should-you-self-host-google-fonts/">This article</a> by <a href="https://twitter.com/tunetheweb">Barry Pollard</a> lays out many of the pros and cons for both approaches. <a href="https://sia.codes/">Sia Karamelagos</a> published an excellent article on <a href="https://sia.codes/posts/making-google-fonts-faster/">Making Google Fonts Faster in 2022</a>, but she recommends self-hosting for full control. The fact that the host can change the font overnight without you knowing is one more argument in favour of self-hosting!</p>
<p>I would imagine bugs like this are pretty rare, and yes, no doubt partly down to using a non-standard CSS property in my case. But it’s definitely persuaded me to host my own fonts where possible from now on.</p>
<h2>Webfont generators</h2>
<p>Font Squirrel has a <a href="https://www.fontsquirrel.com/tools/webfont-generator">webfont generator</a> tool that includes font subsetting, to help generate as small a file as possible.</p>
<p>From Sia’s article I discovered <a href="https://google-webfonts-helper.herokuapp.com/fonts">google-webfonts-helper</a>, which makes it even easier to switch your Google fonts to a self-hosted version. Be sure to <a href="https://sia.codes/posts/making-google-fonts-faster/">read the article</a> for hints and tips to further optimise your fonts using the tool.</p>
New CSS Features In 20222022-03-01T00:00:00Zhttps://css-irl.info/new-css-features-in-2022/<p>There are <strong>a lot</strong> of new CSS features in development as I write this. And 2022 is the year that some big ones will be landing in a browser near you, with the potential for big changes in the way we write CSS! I explored some of these new features for Smashing Magazine.</p>
<p><a href="https://www.smashingmagazine.com/2022/03/new-css-features-2022/">Read the article</a></p>
Are My Third Parties Green?2022-03-01T00:00:00Zhttps://css-irl.info/are-my-third-parties-green/<figure>
<img src="https://css-irl.info/are-my-third-parties-green.webp" alt="Screenshot of the website “Are My Third Parties Green?”" width="1800" height="988" />
<figcaption><a href="https://aremythirdpartiesgreen.com/">Fershad Irani’s tool</a> lets you check if your third-party scripts use green hosting.</figcaption>
</figure>
<p>Recently I’ve been thinking quite a bit about third-party scripts and how much bloat they so often add to the average webpage — and, consequently, their impact on climate change via the additional carbon emissions generated, often for something the end user doesn’t really want or need. Third-party scripts can include ads, trackers, analytics, social media embeds, and probably some other stuff too. An analysis by <a href="https://marmelab.com/">Marmelab</a> found that <strong>up to 70% of the carbon footprint of media websites could be attributed to ads and stats</strong> (<a href="https://marmelab.com/blog/2022/01/17/media-websites-carbon-emissions.html">source</a>).</p>
<p>Often we as developers aren’t in a position to remove these things entirely, and the conversation around web monetisation is a complex one. But we should at least load them as responsibly as possible. (I wrote about <a href="https://www.smashingmagazine.com/2022/02/reducing-web-carbon-footprint-optimizing-social-media-embeds/">some strategies for optimising social media embeds</a> for Smashing Magazine.)</p>
<p>One thing we might not consider, however, is whether the third parties themselves use green web hosting — that is, hosting that is powered by renewable energy. <a href="https://twitter.com/fershad">Fershad Irani</a> has developed a useful tool that analyses the third-party scripts on your website: <a href="https://aremythirdpartiesgreen.com/">Are My Third Parties Green?</a> checks whether they use a green web host (to the best of knowledge, via <a href="https://www.thegreenwebfoundation.org/">The Green Web Foundation</a>’s dataset), as well as how much of your site’s overall size comes from third-party scripts, how effectively they’re cached, and their estimated carbon emissions.</p>
<p>Tools like this are great for shining a light on the environmental impact of our websites. Although measuring the carbon emissions of a website is far from an exact science, projects like this help raise awareness, and enable us to begin conversations and increasingly make informed choices.</p>
<p><a href="https://aremythirdpartiesgreen.com/">Visit the site →</a></p>
A Blog Post About Blogging2022-02-22T00:00:00Zhttps://css-irl.info/a-blog-post-about-blogging/<p>Someone in a Slack group I’m a member of shared recently shared a link to a post by <a href="https://twitter.com/doctorow">Cory Doctorow</a> titled <em><a href="https://doctorow.medium.com/the-memex-method-238c71f2fb46">The Memex Method</a></em>. In the post he shares his opinions on blogging as a way to order your thoughts and ideas, somewhere between a sketchbook full of notes and a fully-formed essay. He writes:</p>
<blockquote>
<p>The very act of recording your actions and impressions is itself powerfully mnemonic, fixing the moment more durably in your memory so that it’s easier to recall in future, even if you never consult your notes.</p>
</blockquote>
<p>That’s certainly the way it works for me. I find the best way to learn about a technical concept is by writing about it. Often I’ll refer back to a blog post I’ve written in the past, to refresh my memory. Sometimes the act of writing itself is enough to preserve it in my mind.</p>
<p>I’d like to get more into the habit of blogging about interesting articles I read, or creative coding demos I come across. Often I’ll come across something on Twitter and retweet it, but I like the idea of having a place where I can easily go back and find the link, without having to scroll back for ages. Cory also writes:</p>
<blockquote>
<p>Every day, I load my giant folder of tabs; zip through my giant collection of RSS feeds; and answer my social telephones — primarily emails and Twitter mentions — and I open each promising fragment in its own tab to read and think about. If the fragment seems significant, I’ll blog it: I’ll set out the context for why I think this seems important and then describe what it adds to the picture.</p>
</blockquote>
<p>I’ve noticed a few other folks doing this: <a href="https://css-tricks.com/">CSS Tricks</a> often shares a link to someone else’s work, with a few paragraphs to explain what they found interesting. <a href="https://twitter.com/bramus">Bramus</a> frequently does it on <a href="https://www.bram.us/">his blog</a>. And I always find a bunch of hidden gems in <a href="https://twitter.com/ohhelloana">Ana Rodrigues</a>’s bookmarks, which she shares on her delightful <a href="https://ohhelloana.blog/">blog</a>.</p>
<p>Writing on this blog is the place I feel I can write freely about what interests me. I’m going to try to share a little more of the things that inspire me. And if you don’t have a blog yourself, I can highly recommend starting one.</p>
<p><a href="https://doctorow.medium.com/the-memex-method-238c71f2fb46">Read the article The Memex Method →</a></p>
In Commission to No Emmissions: Videos from Toronto Web Performance Meetup2022-02-19T00:00:00Zhttps://css-irl.info/in-commission-to-no-emissions-videos-from-toronto-web-performance-meetup/<figure><img src="https://css-irl.info/in-commission-to-no-emissions-01.svg" alt="Line drawing of a loudspeaker" width="1600" height="668" /></figure>
<p>Earlier this month I has the pleasure of speaking at a special green-themed edition of <a href="https://www.meetup.com/Toronto-Web-Performance-Group/">Toronto Web Performance Meetup</a> (online). There were three of us speaking in all, each with a different take on how we as web developers can reduce our environmental impact.</p>
<h2>Web Performance And The Planet</h2>
<p><a href="https://twitter.com/fershad">Fershad Irani</a> spoke about how web performance relates to our site’s carbon emissions, and the concrete actions we can take to build our sites greener. His article <a href="https://fershad.com/writing/cop26-a-quick-sustainability-check/">analysing the COP26 website</a> helped inspire my own article for CSS Tricks on <a href="https://css-tricks.com/reduce-your-websites-environmental-impact-with-a-carbon-budget/">website carbon budgets</a>.</p>
<p><a href="https://www.youtube.com/watch?v=LD8HiUGdsX0">Watch Fershad’s talk →</a></p>
<p><a href="https://speakerdeck.com/fershad/web-performance-and-the-planet">See the slides →</a></p>
<h2>Building a Greener Web</h2>
<p>My own talk was about the wider context around reducing our website’s environmental impact, how we can convince stakeholders to make it a priority, and why individual action matters. I also talked about some of the things we can do beyond making our websites greener, as part of the wider community.</p>
<p><a href="https://www.youtube.com/watch?v=OdiSM9wLPAM&t=6s">Watch the talk →</a></p>
<p><a href="https://noti.st/mbarker84/3lgwsN/building-a-greener-web">See the slides →</a></p>
<h2>The Carbon Footprint of Images</h2>
<p><a href="https://twitter.com/jonarnes">Jon A. Særetås</a> talked about images on the web, and how we can reduce their environmental impact. His talk included some really interesting data, and some surprising details we don’t often consider — like the fact that many of our file live on in the cloud indefinitely, meaning they continue to consume resources.</p>
<p><a href="https://www.youtube.com/watch?v=G-NNziQB34U">Watch Jon’s talk →</a></p>
<p><a href="https://speakerdeck.com/jonarnes/carbon-footprint-of-images-on-the-web">See the slides →</a></p>
<aside>You might notice that this post avoids including YouTube embeds. I recently wrote about the environmental and performance impact of third-party embeds for <a href="https://www.smashingmagazine.com/2022/02/reducing-web-carbon-footprint-optimizing-social-media-embeds/">Smashing Magazine</a>. Linking to the videos instead of embedding them vastly reduced the page weight — and saves you, the user, from downloading unnecessary resources if you choose not to watch the videos!</aside>
I Finally Installed an Ad Blocker2022-02-13T00:00:00Zhttps://css-irl.info/i-finally-installed-an-ad-blocker/<p>It might be unusual for a web developer, but up until recently I’d never had an ad blocker installed. I use Firefox as my main browser, which claims to protect privacy by blocking trackers. And to be honest, I don’t read a lot of articles on desktop, preferring to read the news on mobile while I’m on the go. While you might get one or two pop-ups on mobile, the area available for them is clearly a lot smaller, and I generally find the browsing experience for many of the sites I regularly visit is OK.</p>
<p>Most of the sites I do visit on desktop are for my day-to-day job, or research for this blog, and are built with developers as the target audience. I haven’t noticed any/many annoying popups or slow-loading pages on these sites, probably because I know which sites can be trusted to provide quality content, and the people who build them care about user experience.</p>
<p>So, all in all, I hadn’t massively felt the need for an ad blocker, although I figured it would probably make my browsing experience marginally more pleasant. Another reason why I didn’t install one was because I believe it helps me as a developer to browse the web in a way that’s similar to my end users. I’m reminded of <a href="https://twitter.com/SaraSoueidan/status/1490693841166671882?s=20&t=1bKz5-YQkUdiOIMFQlbmnQ">a tweet</a> from <a href="https://twitter.com/SaraSoueidan">Sara Soueidan</a> recently:</p>
<blockquote>
<p>The fact that I live in a country w/ constant connectivity/Internet issues means that I get to see how slow & unusable most sites are.</p>
<br />
<p> But being the positive person that I am 😃 I see that as an edge & inspiration for my own work.</p>
<br /> <p>The Web should be better. We can do better.</p>
</blockquote>
<p>Yes, many users <strong>do</strong> install ad blockers, but many more do not. I doubt either my parents or grandparents would even know what an ad blocker is. If I’m testing a site, I don’t want to be left with the impression that it’s faster than it actually is for the majority of users, simply because I’m blocking ads. I know many ad blockers allow whitelisting, but having to remember to do that all the time isn’t ideal. Which brings me to another point: Many of the sites I value rely on revenue from unobtrusive ads. I even have small ads on my own site. I want to support the creators that I care about to earn a living through advertising, but remembering to whitelist all of these is a faff.</p>
<p>I changed my mind recently, however, when browsing the web without an ad blocker became unbearable. I’ve been researching for various talks and articles, which has meant visiting a lot of sites I don’t usually rely on. Honestly, the number of popup windows and autoplaying videos I had to close, the number of pages I killed entirely because they were so slow to load that I thought my browser had crashed, came as a shock even to me as a web professional. And that’s on a decent MacBook Pro, with a fibre broadband connection.</p>
<p>We’re used to the web providing us with all kinds of conveniences. But it’s really a miracle that many people with spotty wi-fi, limited data, or older devices can get anything done online at all. I’d estimate that the amount of resources I downloaded from ads far outweighed the actual webpage content on the sites I visited. In a world where we all need to seriously conserve resources and minimise the carbon footprint of our sites, ads and trackers should be the first things to go.</p>
<p>I’m aware that the conversation around monetisation on the web is far from simple. But this state of affairs is surely serving no one.</p>
Aspect Ratio is Great2022-02-08T00:00:00Zhttps://css-irl.info/aspect-ratio-is-great/<p>Maybe it’s just me, but I feel like a lot of the time learning new CSS features doesn’t involve just learning a what a single property does, more like getting to grips with a collection of properties and how they work together — even learning a whole specification. That’s certainly not a complaint from me: it makes sense to consider properties as part of an ecosystem. But I have to confess, I love it when a new CSS property lands in browsers that doesn’t have a steep learning curve and just <em>works</em>, with no fuss. The <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/aspect-ratio"><code>aspect-ratio</code></a> property hits all the right spots there, neatly solving with a single line of CSS something that was, quite frankly, a bit of faff before. It’s been supported in browsers for going on a year now, but with Safari finally catching up in September 2021, we can finally feel confident using it with aplomb.</p>
<h2>Goodbye, padding hack</h2>
<p>In times gone by we needed to write some pretty ugly CSS to get elements to conform to an aspect ratio:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.aspect-box</span> <span class="token punctuation">{</span>
<span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.aspect-box::before</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> block<span class="token punctuation">;</span>
<span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">''</span><span class="token punctuation">;</span>
<span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>
<span class="token property">padding-bottom</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span>100% / <span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--aspect-ratio<span class="token punctuation">,</span> 3 / 2<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.aspect-box > :first-child</span> <span class="token punctuation">{</span>
<span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span>
<span class="token property">top</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token property">right</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token property">bottom</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token property">left</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>We tend to call this the “padding hack” because, well, that’s what it is. The custom property helps us cut down on repetition if we need more than one ratio. Here’s a <a href="https://css-tricks.com/aspect-ratio-boxes/">rundown from CSS Tricks</a>, and a <a href="https://codepen.io/michellebarker/pen/abVpgaa">demo here</a>.</p>
<p>No one in their right mind wants to be writing all that. <a href="https://ratiobuddy.com/">Ratio Buddy</a> is a handy tool that generates the Sass snippet for you.</p>
<h2>Practical usage of <code>aspect-ratio</code></h2>
<p>Using the CSS <code>aspect-ratio</code> property is far simpler: Specify <em>width</em> and <em>height</em> values for the aspect ratio, separated with a slash, or specify a single decimal value. These two aspect ratio values will have the same result:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.aspect-box</span> <span class="token punctuation">{</span>
<span class="token property">aspect-ratio</span><span class="token punctuation">:</span> 3 / 2<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.aspect-box</span> <span class="token punctuation">{</span>
<span class="token property">aspect-ratio</span><span class="token punctuation">:</span> 1.5<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>You can explicitly set a width <em>or</em> height on the element and <code>aspect-ratio</code> will work pretty much as you might expect: Whichever dimension is unspecified will be automatically determined by the aspect ratio. If you set both the width <em>and</em> height to something other than <code>auto</code>, then the aspect ratio no longer applies. That’s not a bug, that’s deliberate and useful behaviour.</p>
<p class="codepen" data-height="400" data-default-tab="result" data-slug-hash="oNoBaPE" data-user="michellebarker" style="height: 400px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/oNoBaPE">
Aspect-ratio</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<h2>Expand to fit</h2>
<p><code>aspect-ratio</code> is both intrinsically and extrinsically sized. That means it’s smart enough to respond to both <strong>content</strong> and <strong>context</strong>. Each of these three boxes has as aspect ratio of <code>2 / 1</code> and an explicit width set. The text content in the third box is longer than the available space, so rather than maintaining the aspect ratio, the element expands vertically to fit the content.</p>
<figure>
<img src="https://css-irl.info/aspect-ratio-is-great-01.jpg" alt="Two blue boxes with short text, one purple box with longer text" />
<figcaption><a href="https://codepen.io/michellebarker/pen/eYegQRG">See the demo</a></figcaption>
</figure>
<p>If we set an explicit height instead, the element doesn’t expand, but instead we get overflow (which we could handle with the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/overflow">CSS <code>overflow</code> property</a> if we choose to).</p>
<figure>
<img src="https://css-irl.info/aspect-ratio-is-great-02.jpg" alt="Two blue boxes with short text, one purple box with longer text that overflows the parent" />
<figcaption><a href="https://codepen.io/michellebarker/pen/WNXRYLp">See the demo</a></figcaption>
</figure>
<h2>Aspect ratio images with object-fit</h2>
<p><code>aspect-ratio</code> really shines when combined with <code>object-fit</code> for sizing images. Use <code>object-fit: cover</code> for gallery-style thumbnails:</p>
<p class="codepen" data-height="389" data-default-tab="result" data-slug-hash="RwjKEGa" data-user="michellebarker" style="height: 389px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/RwjKEGa">
Aspect ratio image gallery</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>Or <code>object-fit: contain</code> for a logo grid:</p>
<p class="codepen" data-height="396" data-default-tab="result" data-slug-hash="vYWgvZK" data-user="michellebarker" style="height: 396px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/vYWgvZK">
Aspect ratio logo grid</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>It’s worth noting that <code>object-fit</code> requires an explicit width and height to be set on the element we want to “fit”. So for the following markup (an image element inside an aspect ratio box):</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>aspect-box<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>https://images.unsplash.com/photo...<span class="token punctuation">"</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Robin on a log<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>If we want the image to fill and cover the aspect ratio box, we’ll need the following CSS:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">img</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> block<span class="token punctuation">;</span>
<span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>
<span class="token property">height</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>
<span class="token property">object-fit</span><span class="token punctuation">:</span> cover<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Quite often we want to put stuff <em>in</em> the aspect ratio box, as above. But we could omit the wrapper and put the aspect ratio slap bang on the image element if we wish, using it instead of either <code>width</code> or <code>height</code>:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">img</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> block<span class="token punctuation">;</span>
<span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>
<span class="token property">aspect-ratio</span><span class="token punctuation">:</span> 3 / 2<span class="token punctuation">;</span>
<span class="token property">object-fit</span><span class="token punctuation">:</span> cover<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h2>Aspect ratio boxes in context</h2>
<p>The above demos use Grid or flexbox for the layout, and for a gallery of images that works perfectly as expected. But what if we have a grid where our aspect ratio boxes only contain text (or perhaps are even empty)? One thing to watch out for is that flex and Grid set <code>align-items: stretch</code> by default. That mean that if we have one grid child with content that’s longer than would fit within the aspect ratio box (and assuming we’ve set an explicit width for those boxes rather than height, the more common scenario), then the other items in the grid will <strong>grow to match the height of the longest item</strong>, ignoring the aspect ratio:</p>
<figure>
<img src="https://css-irl.info/aspect-ratio-is-great-03.jpg" alt="Two blue boxes with short text, one purple box with longer text" />
</figure>
<p>This might be desirable behaviour for our design, and is often quite suitable as a default. If, on the other hand we want items to maintain their aspect ratio, even if one item in the row is taller, then we need to set <code>align-items</code> to something other than the default on the grid or flex container:</p>
<figure>
<img src="https://css-irl.info/aspect-ratio-is-great-01.jpg" alt="Two blue boxes with short text, one purple box with longer text" />
</figure>
<p class="codepen" data-height="416" data-default-tab="result" data-slug-hash="YzEZzeM" data-user="michellebarker" style="height: 416px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/YzEZzeM">
Aspect ratio</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<h2>Minimum aspect ratio</h2>
<p>One place where the default behaviour comes in pretty handy is in a UI component where you have text on one side and an image on the other — a call to action, for instance. If we set an aspect ratio on the image, the text column will be <strong>at least</strong> tall enough to match the height of the image. But if the text is longer, the image will grow to match the height of the text.</p>
<figure>
<img src="https://css-irl.info/aspect-ratio-is-great-04.jpg" alt="Two call-to-actions, the second with longer text" />
</figure>
<p><a href="https://codepen.io/michellebarker/pen/abVJozd?">See the demo</a></p>
<p>We can think of <code>aspect-ratio</code> in this case as behaving like a <strong>minimum</strong>, rather than being fixed.</p>
<h2>Browser support</h2>
<p><a href="https://caniuse.com/?search=aspect-ratio">Browser support</a> is now widespread, but the good thing is that, in most cases, you probably don’t need to provide much in the way of fallbacks. Users will still be able to see your content, it just won’t be sized to perfectly fit your desired aspect ratio. However, in the case of an image gallery that uses object-fit, any particularly tall images would cause the other thumbnails in the row to grow, potentially resulting in some odd cropping.</p>
<figure>
<img src="https://css-irl.info/aspect-ratio-is-great-05.jpg" alt="Image gallery" />
</figure>
<p>By setting <code>align-items: start</code> on the grid container, we can prevent this growing behaviour on the larger item’s siblings.</p>
<figure>
<img src="https://css-irl.info/aspect-ratio-is-great-06.jpg" alt="Image gallery" />
</figure>
<p>If you do need to provide a fallback for older browsers, then a good old feature query should suffice:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.aspect-box</span> <span class="token punctuation">{</span>
<span class="token comment">/* Styles for browsers that don't support aspect-ratio */</span>
<span class="token punctuation">}</span>
<span class="token atrule"><span class="token rule">@supports</span> <span class="token punctuation">(</span><span class="token property">aspect-ratio</span><span class="token punctuation">:</span> 1 / 1<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token selector">.aspect-box</span> <span class="token punctuation">{</span>
<span class="token comment">/* Styles for browsers that support aspect-ratio */</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<h2>Wrapping up</h2>
<p><code>aspect-ratio</code> is a great example of a humble property that fills a long-awaited need in the CSS community. It’s simple to use and behaves in a way that respects content, without the need for any extra CSS.</p>
Reducing The Web’s Carbon Footprint: Optimizing Social Media Embeds2022-02-03T00:00:00Zhttps://css-irl.info/reducing-the-webs-carbon-footprint-optimizing-social-media-embeds/<p>In this article, we’ll look specifically at what we can do to reduce the impact of social media embeds and social sharing widgets — or even some strategies to avoid them altogether. While the spotlight is on reducing the environmental impact, many of these tips will be great for performance too.</p>
<p><a href="https://www.smashingmagazine.com/2022/02/reducing-web-carbon-footprint-optimizing-social-media-embeds/">Read the article</a></p>
Not an NFT artist2022-01-30T00:00:00Zhttps://css-irl.info/not-an-nft-artist/<p>Fair warning, this post doesn’t really conform to my usual content model of frontend/CSS writing. But, as the song goes, “It’s my party, and I’ll write if I want to”. 😁</p>
<p>Like many people, I’ve been reading a lot about the world of crypto and NFTs and trying to get my head around it. I’ve avoided professing a strong opinion publicly, as I honestly don’t feel knowledgable enough. But it’s safe to say, I’m an NFT skeptic.</p>
<p>Don’t get me wrong, I’m a big fan of digital artists getting paid for their work — just not at <strong>any</strong> cost. And the horrendous environmental impact of the Ethereum platform is a far too high a cost in my opinion. I should add, <a href="https://ethereum.org/en/energy-consumption/">Ethereum claims</a> to be committed to moving to proof-of-stake, which will vastly reduce its energy consumption. It’s not there yet, but there are “<a href="https://cleannfts.org/">Clean NFT</a>” platforms that already use PoS technology, as opposed to the much more energy-intensive proof-of-work method used by Ethereum.</p>
<p>Aside from the environmental concerns, the crypto world feels very murky. The documentary <a href="https://www.youtube.com/watch?v=YQ_xWvX1n9g">The Line Goes Up</a> by Dan Olson attempts to demystify crypto, NFTs, DAOs and web3 (and countless other terms you’ll have heard bandied about). Even that’s hard to follow at times, but the takeaway is it pretty much all sounds bad. At the very least, it feels like something that anyone outside the tech bubble will have a very hard time processing, and will likely leave communities with low digital literacy out in the cold. The idea that just <em>anyone</em> can participate in the new crypto economy is laughable.</p>
<p>I’ve yet to encounter a compelling case for NFTs, but as a skeptic I’m open to being proved wrong. It’s just that many of the counter-arguments I’ve seen are published by people with a vested interest in the scene, and who have already staked their money, credibility or reputation on it.</p>
<h2>Now it’s personal</h2>
<p>I’ve been getting invovled in any debates on NFTs so far, as I didn’t feel especially qualified to discuss this complex topic in detail. But a personal experience recently gave me an unwelcome initiation into the NFT scene, when someone took some of my (public) code and used it to mint NFTs, which they intended to sell on the NFT platform OpenSea. The original demo is a <a href="https://codepen.io/michellebarker/pen/KKqYdBN">public Codepen</a>, which I’ve willingly shared, and I even wrote a <a href="https://tympanus.net/codrops/2021/10/04/creating-3d-characters-in-three-js/">tutorial about the process</a>.</p>
<p>I have no issues with someone using my code for learning, creating something similar for fun, or adapting it for their own designs. I never intended to make money from it (aside from the fee I got paid to write the tutorial). But this person had minted a series of generative characters, which were to all intents and purposes identical to my own demo (insofar as generative work can be identical), written a backstory, and created a website and Twitter profile to advertise them, all without so much as asking my permission or crediting me in any way. They didn’t seem ashamed of this in any way, or even aware that what they were doing was shady — they were proud of it, and even tweeted me publicly.</p>
<figure>
<img src="https://css-irl.info/not-an-nft-artist-01.jpg" alt="Three 3D generative characters" />
<figcaption>My original demo. The characters are generative, and different every time you load the page</figcaption>
</figure>
<figure>
<img src="https://css-irl.info/not-an-nft-artist-02.jpg" alt="Screenshot of landing page with very similar characters" />
<figcaption>Part of the landing page created to sell the NFTs.</figcaption>
</figure>
<p>I know I’m far from the only one experiencing art theft as a result of people wanting to make a quick buck in the NFT boom. I suspect generative art is particularly alluring as it allows the creation of multiple unique artworks from the same code. Unfortunately, I also suspect that what the people minting these NFTs are doing is just highly unethical, rather than illegal. There’s probably not much recourse from a legal perspective, as the code is freely available. After a series of exchanges on Twitter (with others springing to my defence) and a takedown request to OpenSea, I’m happy to say the NFTs were removed from the platform and the Twitter profile was deleted (although the <a href="https://www.robwarf.com/">webpage</a> remains at the time of writing).</p>
<p>Plenty of people stole art for profit way before NFTs came along, of course. Many of well-known illustrators have had their work blatantly ripped off by big companies without pay or credit in recent years. But it feels like NFTs have given the opportunity for virtually <em>anyone</em> to steal digital art for profit, as long as they can afford the <a href="https://allthings.how/what-are-gas-fees-for-nfts/">gas fees</a>, as evidenced by the examples in <a href="https://www.theguardian.com/global/2022/jan/29/huge-mess-of-theft-artists-sound-alarm-theft-nfts-proliferates">this Guardian article</a> published just yesterday. They’ve really <strong>democratised art theft</strong>.</p>
<p>I’d love for someone to prove that NFTs are ultimately a force for good, but I haven’t seen any compelling evidence yet. I’m reading as widely as possible, and am willing to change my mind. But until then I’ll continue to side-eye the scene from afar.</p>
Building a Scrollable and Draggable Timeline with GSAP2022-01-03T00:00:00Zhttps://css-irl.info/building-a-scrollable-and-draggable-timeline-with-gsap/<p>The <a href="https://greensock.com/">Greensock</a> animation library’s ScrollTrigger and Draggable plugins can help us create some very cool effects that respond to user interaction. In this article for Codrops we’ll look at how to use them together, to create an interactive timeline that’s both scrollable <em>and</em> draggable. We’ll be building a timeline showing the albums of the rock band Radiohead — but you can choose any subject matter you like!</p>
<p><a href="https://tympanus.net/codrops/2022/01/03/building-a-scrollable-and-draggable-timeline-with-gsap/">Read the article</a></p>
2021 in Review2021-12-31T00:00:00Zhttps://css-irl.info/2021-in-review/<p>Another year gone by, and it’s time to reflect on the highlights and look ahead to some goals for next year. Like <a href="https://hiddedevries.nl/en/blog/">Hidde</a>, I prefer not to dwell too much on the negatives for these end-of-year posts. Suffice to say, in another pandemic year, there have been plenty of lows as well as highs.</p>
<h2>CSS</h2>
<p>There hasn’t been one single new CSS feature in 2021 that’s captured my attention above all others. But CSS continues to evolve and keep pace with developers’ needs. It’s great to see browsers coming through with widespread support for <code>aspect-ratio</code>. I’ve been using <code>min()</code>, <code>max()</code> and <code>clamp()</code> a lot more now that they’re widely supported, and finding them super convenient. And there are <em>a lot</em> of exciting new CSS features just over the horizon — some of which browsers are already beginning to implement. Here are some I’m looking forward to in 2022:</p>
<ul>
<li><a href="https://css-tricks.com/did-you-know-about-the-has-css-selector/"><code>:has()</code></a> (otherwise known as the parent selector)</li>
<li><a href="https://www.smashingmagazine.com/2021/11/guide-modern-css-colors/">LAB, LCH and HWB</a> color functions (currently supported in Safari), along with <code>color-mix()</code> and <code>color-contrast()</code>.</li>
<li><a href="https://ishadeed.com/article/say-hello-to-css-container-queries/">Container queries</a></li>
<li><a href="https://www.smashingmagazine.com/2021/09/simplifying-form-styles-accent-color/"><code>accent-colour</code></a> (which already has limited support)</li>
<li><a href="https://www.smashingmagazine.com/2018/07/css-grid-2/">Subgrid</a> – this has been in Firefox for a while, but finally Chrome is working on implementing it too</li>
<li><a href="https://www.bram.us/2021/09/15/the-future-of-css-cascade-layers-css-at-layer/">Cascade layers</a></li>
</ul>
<h2>People</h2>
<p>There are lots of people who have been doing marvellous work with CSS in 2021, so I’d like to draw your attention to some I’ve been inspired by:</p>
<h3>Ahmad Shadeed</h3>
<p>Ahmad writes a lot of deep dives into using the latest and greatest CSS to solve real-world UI challenges. He’s incredibly prolific too!</p>
<p><a href="https://ishadeed.com/">Read Ahmad’s blog</a></p>
<h3>Adam Argyle</h3>
<p>As a Google Dev Rel, it’s kind of Adam’s job to tinker at the cutting edge of CSS, and I love the absolute joy with which he goes about it. One of my favourite article’s this year is his tutorial on <a href="https://web.dev/building-a-color-scheme/">creating colour schemes with CSS custom properties</a>, and his <a href="https://open-props.style/">Open Props</a> project is pretty cool too.</p>
<h3>Stephanie Eckles</h3>
<p>Stephanie is another incredibly prolific web educator (I know, where do people find the time?!), managing to publish multiple projects on CSS, accessibility, and static site generator <a href="https://www.11ty.dev/">Eleventy</a>. Her projects included <a href="https://moderncss.dev/">Modern CSS</a>, <a href="https://wordwrap.dev/">Word Wrap</a> podcast and, most recently, <a href="https://12daysofweb.dev/">12 Days of Web</a>.</p>
<h3>George Francis</h3>
<p>George has been producing absolutely amazing generative SVG artwork this year. Not only that, but he regularly shares his process in tutorials on <a href="https://georgefrancis.dev/">his beautifully designed blog</a>, including user-friendly interactive demos.</p>
<h3>Amit Sheen</h3>
<p>Amit’s <a href="https://codepen.io/amit_sheen/">Codepen profile</a> is a masterclass in the mind-bending power of CSS. He continues to amaze, with increasingly intricate and elaborate 3D CSS creations, which bring me joy every time they show up in my timeline.</p>
<h3>Miriam Suzanne</h3>
<p><a href="https://www.miriamsuzanne.com/">Miriam</a> is a font of CSS knowledge and has been working hard on <a href="https://www.miriamsuzanne.com/2021/05/02/container-queries/">Container queries</a> and the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@layer">Cascade Layers specification</a>, which we can hopefully look forward to hearing much more about in the coming year!</p>
<h3>Bramus van Damme</h3>
<p>Bramus has an <a href="https://www.bram.us/">awesome blog</a> covering tonnes of new CSS features, and has his finger firmly on the pulse. Definitely the person to follow if you want to hear about the latest CSS features.</p>
<h2>Talks</h2>
<p>I attended one event in person this year: <a href="https://2021.stateofthebrowser.com/">State of the Browser</a> in London. It’s always one of my favourite conferences, so I’m glad I made the effort. One the talks that stayed with me the most was Tom Greenwood’s talk, <em><a href="https://2021.stateofthebrowser.com/speakers/tom-greenwood/">A Sustainable Web For Everyone</a></em> which inspired me to get more involved in raising awareness of the web’s carbon footprint and what we can do to reduce it.</p>
<p>This was the first year in a while that I haven’t given any talks myself, and it’s been a welcome break. No doubt I’ll get back to it at some point.</p>
<h2>Career</h2>
<p>2021 involved a couple of big life changes for me: Firstly, I reduced my working hours as an employee to four days per week, enabling me to spend a full day each week concentrating on writing. This has meant I’ve been able to publish articles on <a href="https://tympanus.net/codrops/">Codrops</a>, <a href="https://www.smashingmagazine.com/">Smashing Magazine</a> and <a href="https://css-tricks.com/">CSS Tricks</a> this year, and it’s been great to regularly get paid for doing something I love. The downside is it’s meant I’ve had less appetite for writing and creating demos in my spare time. I’ve certainly spent fewer evenings in front of a computer screen, which on the whole has been a good thing. But I also miss creating playful CSS demos just for the fun of it, so that’s something I’d like to return to in 2022. Writing for this blog has always been hugely enjoyable, so one of my goals in 2022 is to write a little less for other publications and a little more for my own.</p>
<p>The other big change has been starting a new job. In November I said goodbye to the <a href="https://www.atomicsmash.co.uk/">Atomic Smash</a> team, which I’ve enjoyed being a part of for two years, and began a new role as Senior Front End Developer at <a href="https://www.ada-mode.com/">Ada Mode</a>, building a web app to help wind turbine operators to monitor and maintain their fleet. So far my work has been full of exciting challenges, providing me with plenty of opportunities to learn new skills and stretch my creative muscles. More importantly, I’m thrilled to be working with a company with environmental values at its core.</p>
<h2>Goals for 2022</h2>
<p>As I mentioned, in 2022 I want to find more time to be playful and creative with CSS, and to focus on this blog a bit more. I feel more passionate about a subject when I make time to have fun with it, and I think that shows in my writing too. I also want to dive more deeply into data visualisation and <a href="https://d3js.org/">D3.js</a>, which I’m learning for my job. Perhaps that’ll show up in my writing this year too. But I also want to continue to set boundaries for myself, maintain a decent work-life balance and say “no” to the things I don’t have the time or energy for.</p>
<p>I’ll end this year with a note of appreciation for the wonderful online communities that I feel privileged to be a part of around CSS, web standards and climate action. I’m ever grateful for their support and wisdom, and I don’t know how we’d get through this pandemic without each other.</p>
:has() Has Landed in Safari2021-12-27T00:00:00Zhttps://css-irl.info/has-has-landed-in-safari/<p>Just a few days ago Safari gave us all an early Christmas present: the latest Safari Technology Preview release includes support for the <code>:has()</code> pseudo-class! Otherwise known as the “parent selector”, (but officially termed in the <a href="https://drafts.csswg.org/selectors/#relational">the spec</a> ‘The Relational Pseudo-class’) it allows us to style an element based on its descendants. We can do things like (off the top of my head) style any sections that contain <code>img</code> elements:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">section:has(img)</span> <span class="token punctuation">{</span>
<span class="token property">background</span><span class="token punctuation">:</span> lightgray<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>But eagle-eyed CSS aficionados might spot that it opens up way more possibilities than that, as Bramus writes in <a href="https://www.bram.us/2021/12/21/the-css-has-selector-is-way-more-than-a-parent-selector/">his blog post</a>. He’s also been experimenting with <a href="https://twitter.com/bramus/status/1473429865932238848?s=20">form styling</a>, including styling labels based on the validity of their inputs.</p>
<p>You could even style a whole form differently if it contains invalid inputs:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">form:has(input:invalid)</span> <span class="token punctuation">{</span>
<span class="token property">border</span><span class="token punctuation">:</span> 2px solid red<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>The parent selector is something the CSS world has been crying out for for some time, and I’m sure we’ll see lots more imaginative use cases come to light over the coming year.</p>
<p>It’s worth mentioning that you can test for support for <code>:has()</code> using feature queries with the <code>selector()</code> function, as detailed <a href="https://css-tricks.com/supports-selector/">in this CSS Tricks post</a>. Having never needed to use it before, this had completely passed me by, but it’s yet another cool CSS feature that’ll come in handy. Bramus uses it in his post to show or hide a warning message, depending on whether the browser supports <code>:has()</code>:</p>
<pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@supports</span> <span class="token function">selector</span><span class="token punctuation">(</span><span class="token selector-function-argument selector">:has(*)</span><span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token selector">.info,
.warning</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>On a side note, it’s great to see Safari leading the way on new CSS features like <code>:has()</code>, after years of lagging behind — no doubt spearheaded by Jen Simmons’ presence on the team.</p>
Building a Greener Web2021-12-21T00:00:00Zhttps://css-irl.info/building-a-greener-web/<figure>
<img src="https://css-irl.info/building-a-greener-web.svg" alt="SVG illustration of the earth on a purple background" width="1600" height="693" />
</figure>
<p>As web developers we don’t tend to talk a great deal about the environmental impact of the products we build. But the uncomfortable truth is that the web is responsible for a growing proportion of carbon emissions. Rising temperatures, increasingly extreme weather events, and the failure of this year’s COP26 conference to deliver strong leadership have all highlighted the urgency of reducing our emissions if we’re to avoid disastrous climate change. The commitments of world leaders may have fallen short, but we all have a role to play, and we can’t escape our responsibility.</p>
<h2>Talking honestly about our impact</h2>
<p>When I published <a href="https://css-tricks.com/reduce-your-websites-environmental-impact-with-a-carbon-budget/">an article for CSS Tricks</a> suggesting that perhaps we should set a carbon budget for our websites, I got a <em>lot</em> of responses. Some were broadly supportive, others were inspired to further analyse the impact of our websites, which I love. But there were also many highly critical responses, which can be more or less summarised as “What’s the point when it’s a drop in the ocean compared to [insert tangentially related thing here]”. Indeed, some were comparing the impact of a 2MB website to streaming a large video, and arguing that our efforts are better spent elsewhere. I want to address some of those arguments, because I feel passionately that individual action can and does have an impact, even when seemingly dwarfed by the actions of governments and global corporations.</p>
<h3>Individual action amplifies our voices</h3>
<p>First of all, individual action and putting pressure on larger organisations aren’t mutually exclusive! Many people are doing both, as well as making an effort to reduce their environmental impact beyond the web. I believe it lends more weight to our arguments if we can demonstrate an appetite for individual effort too. Without it, it’s much easier for politicians and corporations to dismiss our calls for action as a niche interest.</p>
<h3>Talking about this stuff helps</h3>
<p>The fact that we’re talking about this is a good thing. It’s one way of applying pressure and making this issue visible. We might not have all the answers, but let’s not stop having the conversation. Making seemingly small changes to improve the carbon footprint of a website is a great starting point for having those conversations and demonstrating things that could have an even greater impact when done on a wider scale.</p>
<h3>Working collectively is better</h3>
<p>When many people pool their resources towards a shared goal, it becomes a movement. Communities like <a href="https://climateaction.tech/">ClimateAction.tech</a> are growing and becoming stronger through their members sharing knowledge and spurring each other on. To use a climate-adjacent analogy, I recycle, not because I believe that me individually sorting my tins and plastics makes a big difference, but because it can <em>only</em> work if we do it collectively.</p>
<p><a href="https://hotorcool.org/hc-posts/debunking-the-false-choice-between-individual-behavior-change-and-systems-change/">This article</a> from the think tank <a href="https://hotorcool.org/">Hot or Cool</a> raises many good points about how individual action <em>and</em> systematic change are both important:</p>
<blockquote>
<p>In any society, individuals drive social norms that make up the collective culture. For instance, cultural revolutions don’t happen because of systems change; they happen when a group of people voice a compelling story that propagates across society and becomes a social norm.</p>
</blockquote>
<p>Another great quote comes from the article <em><a href="https://heylow.world/notes/the-carbon-footprint-of-the-internet-making-the-invisible-visible">The carbon footprint of the Internet: making the invisible visible</a></em> by low-carbon web agency <a href="https://heylow.world/">Hey Low</a>:</p>
<blockquote>
<p>A website's overall impact is minor, as is the effect of a single plastic bag. It makes no difference if there are one or two more. Isn't it simply a plastic bag? We all know now that it's not about just one plastic bag (or one website) - it's the impact of millions of plastic bags, millions of websites. However restrictions are starting to appear for plastic, a tangible and visible material, which the bulk of the internet stays hidden (with no restrictions).</p>
</blockquote>
<h3>There’s always more that can be done</h3>
<p>I’m not <em>just</em> talking about optimising a blog that attracts a few hundred visitors (although that itself is still worth doing). Taking Netflix as an example — as several people mentioned streaming a Netflix video as an activity that would arguably have a worse impact than a 2MB website: Netflix, is in fact, a website. According to Netflix’s own <a href="https://s22.q4cdn.com/959853165/files/doc_downloads/2021/03/2020-SASB-Report_FINAL.pdf">sustainability report</a>, the company uses 100% renewable energy for its internet services. That’s great, but I’d be willing to bet there’s much more they could do too. What about the energy used by users’ devices? By optimising their content they could have an impact here too. Of course, we should always be mindful of corporate greenwashing, and take every claim with a pinch of salt.</p>
<p>It’s easy to fall into the trap of thinking “we can’t do everything, so we might as well do nothing”. Netflix’s sustainability report states that streaming accounts for only 5% of their carbon footprint, with a much higher proportion coming from content production and corporate operations. Does that mean they shouldn’t bother trying to reduce the impact their streaming emissions? I would argue not. There’s always going to be a bigger target to hit, but it’s illogical to argue that it means we shouldn’t bother trying to hit the smaller ones.</p>
<h3>We can only do what’s within our control — but the impact might surprise you</h3>
<p>Sustainability is a huge topic that impacts every area of our lives. But most of us are only experts at one or two things. Individually we can’t fix all the problems, but we can help fix some things in the areas we know.</p>
<p><a href="https://dannyvankooten.com/website-carbon-emissions/">This case study</a> by Danny van Kooten details the amount of C02 he saved by optimising his WordPress plugins. Used by over 2 million websites, the savings soon add up, as he explains:</p>
<blockquote>
<p>Shaving off a single kilobyte in a file that is being loaded on 2 million websites reduces CO2 emissions by an estimated 2950 kg per month.</p>
</blockquote>
<h3>Reducing the carbon footprint of your sites might even have tangible benefits right now</h3>
<p>More and more governments and organisations are requiring environmental impact reports and/or setting sustainability as a criteria for winning contracts. For example, in the UK any new digital services commissioned by the government above a certain threshold need to meet the outcomes defined in the Greening <a href="https://www.gov.uk/government/publications/greening-government-ict-and-digital-services-strategy-2020-2025">Government ICT and Digital Services Strategy</a>. Even if you don’t care about reducing the carbon footprint of your web projects, it might be in your financial interest to do it anyway.</p>
<h3>Inaction achieves nothing</h3>
<p>One thing that we can be sure of is it’s better to contribute a small amount to the collective good than to do nothing. Big changes are never achieved by throwing our hands up in despair, only by getting to work. Rather than a drop in the ocean, it’s more productive to think of our actions as a snowball rolling down a hill, getting bigger and gaining momentum.</p>
<p>There are plenty of people who won’t be convinced by my arguments and whose minds I will never change, but by writing about this I hope I can convince just a few people that making an effort to build a greener web is worth it.</p>
<h2>Resources</h2>
<p>If you’re interested in learning more about how we can reduce the environmental impact of the websites we build and/or taking action, here are some resources:</p>
<h3>Talk: A Sustainable Web For Everyone</h3>
<p>This talk from State of the Browser conference by <a href="https://twitter.com/eatwholegrain">Tom Greenwood</a> got me thinking a lot about building sustainable websites. I recommend his book, <a href="https://abookapart.com/products/sustainable-web-design">Sustainable Web Design</a> too.</p>
<p><a href="https://2021.stateofthebrowser.com/speakers/tom-greenwood/">Watch the talk ⟶</a></p>
<h3>ClimateAction.tech</h3>
<p>A community of tech workers using their skills to take action against climate change. There’s a Slack group, action guides, events and projects to get involved with, and they publish <a href="https://branch.climateaction.tech/">Branch</a> magazine.</p>
<p><a href="https://climateaction.tech/">Join the community ⟶</a></p>
<h3>The Green Web Foundation</h3>
<p>The Green Web Foundation produces tools and collects data with aim of aiding the transition towards an internet run entirely on renewable energy.</p>
<p><a href="https://www.thegreenwebfoundation.org/">Visit the website ⟶</a></p>
<h3>The Green Software Foundation</h3>
<p>A non-profit dedicated to reducing emissions in the software industry. Consisting of four working groups, their mission is to:</p>
<blockquote>
<p>create a trusted ecosystem of people, standards, tooling, and best practices for building green software.</p>
</blockquote>
<p><a href="https://greensoftware.foundation/articles/welcome-to-the-green-software-foundation">Learn about the Green Software Foundation ⟶</a></p>
<h3>The Sustainable.dev</h3>
<p>Developer resources for building more sustainable websites, and a hiring directory for developers with green credentials.</p>
<p><a href="https://the-sustainable.dev/">Visit the-sustainable.dev ⟶</a></p>
<h3>Podcast: How to Save a Planet</h3>
<p>I’ve been recommended <a href="https://gimletmedia.com/shows/howtosaveaplanet/llh8gxg/is-your-carbon-footprint-bs">this particular episode</a> breaking down the argument of systematic change versus personal responsibility. (Disclaimer: I haven’t had the chance to listen yet).</p>
<p><a href="https://gimletmedia.com/shows/howtosaveaplanet">Check out the podcast ⟶</a></p>
<h3>Tools for measuring your website’s environmental impact</h3>
<ul>
<li><a href="https://ecoping.earth/">Ecoping</a></li>
<li><a href="https://www.websitecarbon.com/">Website Carbon Calculator</a></li>
</ul>
<aside><p>This article was updated on 23rd December 2021 to include additional resources. Thank you to the members of <a href="https://climateaction.tech/">CimateAction.tech</a> for their contributions.</p></aside>
Reduce Your Website’s Environmental Impact With a Carbon Budget2021-12-06T00:00:00Zhttps://css-irl.info/reduce-your-websites-environmental-impact-with-a-carbon-budget/<p>In this article for CSS Tricks’ end-of-year series I wrote about how we in the web development industry should examine the environmental impact of the products we’re building, and some resources to help us do so.</p>
<p><a href="https://css-tricks.com/reduce-your-websites-environmental-impact-with-a-carbon-budget/">Read the article</a></p>
Don’t Forget the “lang” Attribute2021-12-02T00:00:00Zhttps://css-irl.info/dont-forget-the-lang-attribute/<p>An interesting notification from <a href="https://twitter.com/cyishere">@cyishere</a> popped up in my Twitter feed the other day:</p>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">I use a Chrome extension Read Aloud to read the web pages for improving my English. The reading voice for most web pages is American accent, but when it reads <a href="https://twitter.com/CSSInRealLife?ref_src=twsrc%5Etfw">@CSSInRealLife</a>, it switches to British, which is very interesting.😁</p>— CY is here 👩🏻💻🚀🖖🏻 (@cyishere) <a href="https://twitter.com/cyishere/status/1465312612074147847?ref_src=twsrc%5Etfw">November 29, 2021</a></blockquote> <script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>I replied that I thought this was likely due to the page’s <code>lang</code> attribute, which on this site is <code>en-GB</code> — British English. The default for many frameworks is <code>en</code>, (general English) or <code>en-US</code> (American English). While there might not be huge differences between those and British English, it can affect how certain words are pronounced to assistive technologies such as screenreaders. Of course, it would have a much bigger impact on web pages that are in a different language altogether, so you should always make sure you change it to the appropriate value!</p>
<h3>Update</h3>
<p><a href="https://twitter.com/KittyGiraudel">Kitty Giraudel</a> mentioned that it might be best to use <code>en</code> rather than localised English (such as <code>en-GB</code> or <code>en-US</code>), as it allows people using screenreaders to pick the locale that they’re used to. (<a href="https://twitter.com/KittyGiraudel/status/1466684618309001219?s=20">Link to tweet</a>.) The only source I can find for this is the comments section of a 2015 by Adrian Roselli (<a href="https://adrianroselli.com/2015/01/on-use-of-lang-attribute.html">On Use of the Lang Attribute</a>). It sounds like a good call, but I’m not sure there’s a consensus. It seems not to affect spellchecking, as the browser’s settings override it. When I view <a href="https://codepen.io/michellebarker/pen/XWebGmO">this demo</a> in Chrome the word “color” (American English spelling) is underlined as being incorrectly spelled, but in Firefox it’s the British English spelling, “colour”.</p>
<p>You can <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang">read more about the <code>lang</code> attribute</a> on MDN.</p>
<p>The experience shared by <a href="https://twitter.com/cyishere">@cyishere</a> serves an important reminder that people browse the web in all sorts of ways we might not be aware of when building our sites, and we should make sure we build with that flexibility in mind. While it’s easy to see this unknowability as a hindrance, I believe that it’s something to be embraced. In building for the web, we have the opportunity to deliver content to users in a way that suits them, and the web platform provides us with all sorts of features in HTML and CSS that help us do that. Starting with semantic HTML and layering on top with <a href="https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement">progressive enhancement</a> is a solid strategy.</p>
<p>Speaking of the <code>lang</code> attribute reminded me of <a href="https://twitter.com/mmatuzo">Manuel Matuzović</a>’s excellent article <a href="https://www.matuzo.at/blog/html-boilerplate/">analysing his HTML boilerplate</a>. It contains tons of useful information about many of the elements and attributes most of us don’t give much thought to day-to-day if, like Manuel, we recycle the same boilerplate from project to project. It’s well worth a read.</p>
<aside>This article was updated on <time>2nd December 2021</time>. Special thanks to <a href="https://twitter.com/KittyGiraudel">Kitty Giraude</a>, <a href="https://twitter.com/fantasai">Fantasai</a> and <a href="https://twitter.com/hdv">Hidde de Vries</a> for adding to the discussion and pointing some things out to me! <a href="https://twitter.com/CSSInRealLife/status/1466344822000828418?s=20"></a>Read the thread</aside>
A Guide To Modern CSS Colors With RGB, HSL, HWB, LAB And LCH2021-11-18T00:00:00Zhttps://css-irl.info/a-guide-to-modern-css-colors/<p>There’s more to color on the web than meets the eye, and it’s about to get a lot more interesting! In this article, we’ll take a look at the best ways to use colors in a design system, and what we can expect from our colors in the not-too-distant future.</p>
<p><a href="https://www.smashingmagazine.com/2021/11/guide-modern-css-colors/#comments-guide-modern-css-colors">Read the article</a></p>
IT Career Energizer Podcast2021-11-17T00:00:00Zhttps://css-irl.info/it-career-energizer-podcast/<figure>
<img src="https://css-irl.info/it-career-energizer-podcast-01.png" alt="Profile image with the text 'IT Career Energizer Podcast Episode 318: Michelle Barker, Lead Front End Developer at Atomic Smash — Learn From Your Communities and Remember That Learning Is Fun'" loading="lazy" width="1200" height="630" />
</figure>
<p>I recently had the priviledge of chatting to Phil Burgess on the <a href="https://itcareerenergizer.com/">IT Career Energizer Podcast</a> about my journey into web development, and advice to new developers starting out on that path. You can <a href="https://itcareerenergizer.com/podcast/learn-from-your-communities-and-remember-that-learning-is-fun-with-michelle-barker/">listen to the episode here</a> — it’s also available on all your favourite podcast platforms (Spotify, Apple Podcasts and more)!</p>
Favourite Web Development Courses2021-11-16T00:00:00Zhttps://css-irl.info/favourite-web-development-courses/<p>I’m not one for taking loads of courses. It’s hard to find the time, and when it comes to learning I always feel that, for me, <strong>doing</strong> beats <strong>reading</strong> any day. That said, I’ve dipped into a few great courses recently that I’ve learned loads from, so I thought I’d share them with you fine folks here.</p>
<h2>Three.js Journey</h2>
<figure>
<img src="https://css-irl.info/favourite-web-development-courses-01.jpg" alt="Screenshot of the Three.js Journey website" loading="lazy" width="1200" height="800" />
</figure>
<p>As someone who’s very comfortable in the DOM but not accustomed to writing spectacularly creative JS, I’ve always found WebGL a little intimidating. If you too have hesitated to take the plunge, I can’t recommend this course highly enough. <a href="https://threejs-journey.com/">Three.js Journey</a> by Bruno Simon ticks <strong>all</strong> of my boxes: Every lesson is available in both video and written format, with multi-speed video playback options, and downloadable project files. All concepts are superbly well explained, with no prior assumptions about the user’s level of knowledge (an all too common problem). Bruno’s conversational style makes it easy to imagine how you might apply the ideas to projects of your own. And the course platform itself is beautifully designed and a pleasure to use.</p>
<p>The course is <strong>really</strong> good value too at under $100, especially for such high quality content. There are a lot of lessons, and Bruno has just added a few more. An absolute must for anyone wanting to get into Three.js.</p>
<p><a href="https://threejs-journey.com/">Three.js Journey website ⟶</a></p>
<h2>Fullstack D3 and Data Visualization</h2>
<p>You might recognise Amelia Wattenberger as the author behind several beautifully illustrated, interactive articles (such as this one on <a href="https://wattenberger.com/guide/scaling-svg">Scaling SVG Elements</a>). She’s also a data visualisation expert, and has published a video course and accompanying book, <a href="https://www.newline.co/fullstack-d3">Fullstack D3 and Data Visualization</a>. As someone who learns better from written tutorials, I only bought the book, but the videos look fantastic too. The video course is broken down into bitesize lessons of no more than 15 minutes each, which I love, as it gives you the chance to really digest the teaching and explore the concepts on your own if you wish to. The book and video course come with downloadable code. This course wastes no time and aims to start you coding right away, whilst taking care to explain everything carefully, and unpacking some helpful tips to set you up for creating charts in the most robust way.</p>
<p>There’s also the option to “try before you buy” — the first few videos are available for free, so you can feel confident in your purchase.</p>
<p><a href="https://www.newline.co/fullstack-d3">Fullstack D3 and Data Visualization website ⟶</a></p>
<h2>CSS For Javascript Developers</h2>
<figure>
<img src="https://css-irl.info/favourite-web-development-courses-02.jpg" alt="Screenshot of the CSS For Javascript Developers website with the text 'Stop wrestling with CSS'" loading="lazy" width="1200" height="800" />
</figure>
<p>Confession: I haven’t actually taken this course, <a href="https://css-for-js.dev/">CSS For Javascript Developers</a> by Josh W Comeau, but I’ve had a sneak peek. Knowing the quality of the articles Josh writes on <a href="https://www.joshwcomeau.com/">his website</a>, this course will be an absolute goldmine of CSS knowledge — it’s already attracting rave reviews. Josh is a great teacher, and doesn’t skimp on the design and real code examples either.</p>
<p>Whether you’re a JS dev who’s never quite got a handle on CSS fundamentals, a seasoned UI developer looking to up their game, or someone who’s ever thought to themselves “Why is CSS so weird?”, there will be plenty to take away from this course. I’m tempted to take it myself, and I consider myself someone with a fairly decent grasp of CSS. (Seriously. The only thing stopping me is the lack of hours in a day.) The price for the full package is a little higher than the other courses mentioned here, but there are different payment tiers available. And, in my humble opinion, that knowledge is absolutely worth forking out for.</p>
<p><a href="https://css-for-js.dev/">CSS For Javascript Developers website ⟶</a></p>
<h2>ES6 For Everyone</h2>
<p>Although it’s a few years old now, this is a course that really helped me level up my JS, and is still super relevant. The <a href="https://es6.io/">ES6 For Everyone</a> video course by Wes Bos is packed with knowledge, covering array methods, promises, async/await, modern tooling and more. Again, the videos are nice and short, and it’s easy to cherry pick the bits you need if you feel more confident in some areas than others.</p>
<p><a href="https://es6.io/">ES6 For Everyone website ⟶</a></p>
Accessible Toggles2021-11-01T00:00:00Zhttps://css-irl.info/accessible-toggles/<p>I recently received some great advice from <a href="https://www.scottohara.me/">Scott O’Hara</a> on improving the accessibility of a <a href="https://codepen.io/michellebarker/pen/bGrNjgB">demo</a> featuring a reduced-motion toggle (for <a href="https://www.smashingmagazine.com/2021/10/respecting-users-motion-preferences/">this article</a>). The demo sets the play-state of the animation depending on the user’s motion preferences (using the <code>prefers-reduced-motion</code> media query). Users could also click the button to toggle motion on and off, which also changes the text of the button to “Turn on motion” or “Turn off motion”. Here’s the original version:</p>
<p class="codepen" data-height="529" data-default-tab="result" data-slug-hash="JjyNXQZ" data-user="michellebarker" style="height: 529px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/JjyNXQZ">
Reduced-motion toggle</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>Scott pointed out that some screenreaders wouldn’t announce the change to the button text when the button is clicked.</p>
<blockquote>
<p>I have one suggestion though, per the accessibility of the toggle control. Changing the accessible name of the button will not be consistently announced by screen readers. NVDA and JAWS in particular do not announce the name change - I made a fork of your pen to demonstrate an alternative way to code the toggle control.</p>
</blockquote>
<p>Let’s walk through the code and add Scott’s suggested improvements with a simpler version of the demo, so we can understand what is going on more clearly from an accessibility point of view. For the purpose of this article we’ll just focus on a button that plays or pauses the animation when clicked, without concerning ourselves with the additional complexity of the original, which includes checking the user’s system-level motion preferences and <code>localStorage</code>. (If you’re interested you can always go back and explore the <a href="https://codepen.io/michellebarker/pen/bGrNjgB">final demo</a> and accompanying article.)</p>
<p>This is the demo we’ll use as a starting point:</p>
<p class="codepen" data-height="461" data-default-tab="result" data-slug-hash="rNzmLGv" data-user="michellebarker" style="height: 461px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/rNzmLGv">
Reduced-motion toggle (basic)</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>Here’s the button in our HTML:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">data-toggle</span> <span class="token attr-name">hidden</span><span class="token punctuation">></span></span>Turn off motion<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></code></pre>
<p>It includes the <code>hidden</code> attribute, as without JS the button doesn’t do anything, and we wouldn’t want to confuse users who don’t have JS enabled, or for whom JS fails to load. So we’ll hide it initially, then remove this attribute with JS.</p>
<p>In our JS code, we first set a variable for whether the animation should currently be paused, with an initial value of <code>false</code> (as the animation will be playing to begin with). We’ll display our button by removing the <code>hidden</code> attribute.</p>
<p>When the button is clicked, we’ll toggle the <code>shouldPauseAnimation</code> variable. Then we’ll set a custom property (to change the play state of the animation in CSS), and update the button text:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> toggle <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'[data-toggle]'</span><span class="token punctuation">)</span>
<span class="token keyword">let</span> shouldPauseAnimation <span class="token operator">=</span> <span class="token boolean">false</span>
<span class="token comment">// The button is hidden initially, as it won't work without JS. We need to make it visible</span>
toggle<span class="token punctuation">.</span>hidden <span class="token operator">=</span> <span class="token boolean">false</span>
toggle<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
shouldPauseAnimation <span class="token operator">=</span> <span class="token operator">!</span>shouldPauseAnimation
<span class="token keyword">if</span> <span class="token punctuation">(</span>shouldPauseAnimation<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment">// Pause animation</span>
toggle<span class="token punctuation">.</span>innerText <span class="token operator">=</span> <span class="token string">'Turn on motion'</span>
document<span class="token punctuation">.</span>body<span class="token punctuation">.</span>style<span class="token punctuation">.</span><span class="token function">setProperty</span><span class="token punctuation">(</span><span class="token string">'--playState'</span><span class="token punctuation">,</span> <span class="token string">'paused'</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
<span class="token comment">// Play animation</span>
toggle<span class="token punctuation">.</span>innerText <span class="token operator">=</span> <span class="token string">'Turn off motion'</span>
document<span class="token punctuation">.</span>body<span class="token punctuation">.</span>style<span class="token punctuation">.</span><span class="token function">setProperty</span><span class="token punctuation">(</span><span class="token string">'--playState'</span><span class="token punctuation">,</span> <span class="token string">'running'</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<p>This toggles the custom property used in the CSS like so, with its original default value:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.circle</span> <span class="token punctuation">{</span>
<span class="token property">animation-play-state</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--playState<span class="token punctuation">,</span> running<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>We now have a working motion toggle (hooray!). Unfortunately, as Scott explained, some screenreaders won’t announce the updated button text on click. Let’s add Scott’s improvements.</p>
<p>First, we’ll update the button’s HTML, so the text that informs the user of the current “on/off” state is inside a <code><span></code> with the <code>aria-hidden</code> attribute:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">data-toggle</span> <span class="token attr-name">hidden</span><span class="token punctuation">></span></span>
Toggle motion
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">aria-hidden</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span> <span class="token attr-name">data-btn-text</span><span class="token punctuation">></span></span>: Off<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></code></pre>
<p>To a screenreader, the button will now simply say “Toggle motion”, with no indication of the state. We’ll give the button a role of <code>switch</code>, and add the <code>aria-checked</code> attribute with a value of <code>true</code>, to indicate an “on” state:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>button</span> <span class="token attr-name">data-toggle</span> <span class="token attr-name">hidden</span> <span class="token attr-name">role</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>switch<span class="token punctuation">"</span></span> <span class="token attr-name">aria-checked</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
Toggle motion
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">aria-hidden</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span> <span class="token attr-name">data-btn-text</span><span class="token punctuation">></span></span>: On<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>button</span><span class="token punctuation">></span></span></code></pre>
<p>In our JS code, we’ll update both the text inside the <code><span></code>, and the <code>aria-checked</code> attribute (in addition to toggling the <code>--playState</code> custom property:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> toggle <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'[data-toggle]'</span><span class="token punctuation">)</span>
<span class="token keyword">const</span> buttonText <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'[data-btn-text]'</span><span class="token punctuation">)</span>
<span class="token keyword">let</span> shouldPauseAnimation <span class="token operator">=</span> <span class="token boolean">false</span>
toggle<span class="token punctuation">.</span>hidden <span class="token operator">=</span> <span class="token boolean">false</span>
toggle<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
shouldPauseAnimation <span class="token operator">=</span> <span class="token operator">!</span>shouldPauseAnimation
<span class="token keyword">if</span> <span class="token punctuation">(</span>shouldPauseAnimation<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment">// Pause animation</span>
buttonText<span class="token punctuation">.</span>innerText <span class="token operator">=</span> <span class="token string">': Off'</span>
toggle<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">'aria-checked'</span><span class="token punctuation">,</span> <span class="token string">'false'</span><span class="token punctuation">)</span>
document<span class="token punctuation">.</span>body<span class="token punctuation">.</span>style<span class="token punctuation">.</span><span class="token function">setProperty</span><span class="token punctuation">(</span><span class="token string">'--playState'</span><span class="token punctuation">,</span> <span class="token string">'paused'</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
<span class="token comment">// Play animation</span>
buttonText<span class="token punctuation">.</span>innerText <span class="token operator">=</span> <span class="token string">': On'</span>
toggle<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">'aria-checked'</span><span class="token punctuation">,</span> <span class="token string">'true'</span><span class="token punctuation">)</span>
document<span class="token punctuation">.</span>body<span class="token punctuation">.</span>style<span class="token punctuation">.</span><span class="token function">setProperty</span><span class="token punctuation">(</span><span class="token string">'--playState'</span><span class="token punctuation">,</span> <span class="token string">'running'</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<p>The result is a more accessible demo:</p>
<p class="codepen" data-height="455" data-default-tab="result" data-slug-hash="oNeGYEz" data-user="michellebarker" style="height: 455px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/oNeGYEz">
Toggle switch for motion (basic) with accessibility improvements</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>The same principles could be applied to many different types of toggles, such as a dark/light mode toggle.</p>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/Switch_role">Read more about the <code>aria-switch</code> role</a>.</p>
Respecting Users’ Motion Preferences2021-10-21T00:00:00Zhttps://css-irl.info/respecting-users-motion-preferences/<p>The prefers-reduced-motion media query has excellent support in all modern browsers going back a couple of years. In this article, we’ll look at how to use it today to make your sites more accessible.</p>
<p><a href="https://www.smashingmagazine.com/2021/10/respecting-users-motion-preferences/">Read the article</a></p>
The State of CSS 20212021-10-20T00:00:00Zhttps://css-irl.info/the-state-of-css-2021/<p>There has arguably never been a better time to be working with CSS. We have so many more tools available at our disposal than even just a couple of years ago. Scroll snap, Motion Path, Level 5 Media Queries and CSS functions <code>min()</code>, <code>max()</code> and <code>clamp()</code> are just some of the CSS features that have got me excited in the last year or two, but even these are old-hat compared to the buzz around <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Container_Queries">container queries</a>, or <a href="https://lea.verou.me/2020/04/lch-colors-in-css-what-why-and-how/">LCH colours</a>, for instance. It can be hard to keep up with the pace of change, but as a developer I would argue that one of the most important qualities to have is curiosity. That doesn’t mean instantly adopting new tools and features the second they arrive (and burning yourself out trying to keep up). It means being open to the possibility that there might be a better way of doing something, and being able to evaluate it objectively when the need arises.</p>
<p>I’ll admit, there are plenty of CSS properties that fly under my radar, simply because I’ve never had a reason to use them. That’s okay, because I know that when the time comes where I’m not sure of the best way to implement something, I’ll undoubtedly come across what I need via a quick search, and that’s when I’ll have the opportunity to learn.</p>
<p>It’s helpful for browser vendors and spec authors to get a good picture of the tools and features developers are using day-to-day, so they know what to prioritise. That way, we’ll hopefully get much-longed-for features faster. Surveys are one way to get a broad picture of the industry, and the annual <a href="https://survey.stateofjs.com/survey/state-of-css/2021">State of CSS survey</a> aims to provide just that. When I took this survey I was surprised by how many CSS properties I wasn’t aware of, which led to me Googling and learning a few things!</p>
<p>No survey is perfect, and there is invariably some degree of bias encoded into the results, depending on the pool of participants. It’s important that the range of opinions is representative of the many different people working in the industry, so capturing as diverse an audience as possible is crucial. So I encourage you, if you work with CSS in any degree, to take the survey today — your opinion counts.</p>
<p><a href="https://survey.stateofjs.com/survey/state-of-css/2021">Take the survey →</a></p>
Evaluating Clever CSS Solutions2021-10-19T00:00:00Zhttps://css-irl.info/evaluating-clever-css-solutions/<p><a href="https://twitter.com/shadeed9">Ahmad Shadeed</a> recently published an <a href="https://www.ishadeed.com/article/conditional-border-radius/">article</a> where he dug into Facebook’s implementation of the <code>border-radius</code> property on their card components. He had noticed, upon inspecting Facebook’s CSS, that the value for <code>border-radius</code> seemed quite convoluted for what amounted to a value of <code>8px</code>:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.card</span> <span class="token punctuation">{</span>
<span class="token property">border-radius</span><span class="token punctuation">:</span> <span class="token function">max</span><span class="token punctuation">(</span>0px<span class="token punctuation">,</span> <span class="token function">min</span><span class="token punctuation">(</span>8px<span class="token punctuation">,</span> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token punctuation">(</span>100vw - 4px - 100%<span class="token punctuation">)</span> * 9999<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>After some investigation, Ahmad discovered that this line of CSS was, in fact, a “toggle” — where under some conditions (namely, when the card was less than the full width of the viewport), the toggle would be “flipped”, and the computed value evaluating to <code>0px</code>. (<a href="https://www.ishadeed.com/article/conditional-border-radius/">Read the full article</a> for details of how this works.)</p>
<h2>Toggles in CSS</h2>
<p>Toggles aren’t an entirely new idea in CSS. Ana Tudor may have been the first person to explore the idea of CSS toggles with her demos on <a href="https://css-tricks.com/dry-state-switching-with-css-variables-fallbacks-and-invalid-values/">DRY State Switching</a>, where she uses a single custom property with a value of <code>0</code> or <code>1</code> to toggle between valid and invalid computed values. Lea Verou discusses a similar idea in her article, <em><a href="https://lea.verou.me/2020/10/the-var-space-hack-to-toggle-multiple-values-with-one-custom-property/#more-3162">The --var: ; hack to toggle multiple values with one custom property</a></em>, this time using whitespace to cause declarations to evaluate to invalid values in order to switch them “off”.</p>
<p>The examples discussed in these articles aren’t quite in the same realm as Facebook’s <code>border-radius</code> implementation, as they don’t involve conditionally changing properties based on the size or layout, but rather a switch flipped “on” or “off” under the developer’s chosen conditions (such as on every <em>nth</em> child, or within a media query). But they do introduce the idea of toggling different properties based on a single value, and it’s not too much of a leap to see how toggles could be implemented in other ways.</p>
<p>The Facebook solution has more in common with Heydon Pickering’s <a href="https://heydonworks.com/article/the-flexbox-holy-albatross/">Flexbox Holy Albatross</a> (also mentioned in Ahmad’s article as the inspiration behind the method), in the way that it uses an unusually large value (say, 9999) to force a value to evaluate to either 0 or something other than 0. In Facebook’s case the CSS <code>min()</code> function then picks the smallest value from a list that includes the calculated value, then the <code>max()</code> function picks the largest from a list including <em>that</em> value. Got that? Me neither...</p>
<h2>How clever is too clever?</h2>
<p>While undoubtedly clever, and super interesting to read about, I side with Robin Rendle in the <a href="https://css-tricks.com/newsletter/272-jams/">CSS Tricks newsletter</a> when he says:</p>
<blockquote>
<p>I can’t help but feel that it’s a little <em>too</em> smart.</p>
</blockquote>
<p>I have to agree here. Tricks like this have their place, and Facebook (which can clearly afford to hire the best of the best CSS developers) might be one of them. But speaking personally, when forced to pick between a trick like this and an ever-so-slightly less optimal but far more readable solution (say, a media query), in 99% of cases I’d plump for the latter. There are some reasons for this: I work at an agency, and most of the code I write will need to be maintained long-term by several developers with various levels of CSS knowledge. Most of their work will involve fixing bugs and building new features at the client’s request, within a limited budget. If the client asks to change the border radius on the cards and they’re faced with this monstrosity? They don’t have time to do an afternoon’s worth of research to make what should be a simple change. But hey, if it’s your personal project and no-one else has to maintain that code, go right ahead. Whatever works for you.</p>
<p>Where I’m <em>hoping</em> Robin is wrong is this part:</p>
<blockquote>
<p>if every little property becomes as complex as the launch codes for a nuclear arsenal then I can’t help but feel that we’re doing something wrong here, that maybe the language is headed in the wrong direction.</p>
</blockquote>
<p>It feels like we’re in a weird interim period of CSS, where we have a lot of cool stuff we didn’t have before, but we don’t yet have <em>everything</em> we need to implement solutions like this elegantly. <strong>But</strong> the CSS gods are listening to us! There are some CSS specifications currently in draft that hopefully in the not-too-distant future will revolutionise how we deal with conditional property values like this, and eliminate the need for hacks. <a href="https://www.stefanjudis.com/blog/conditional-border-radius-and-three-future-css-features/">Read Stefan Judis’ article</a> on how the above code might look when refactored to use <a href="https://drafts.csswg.org/css-contain-3/">container queries</a> or <a href="https://tabatkins.github.io/specs/css-when-else/">when/else rules</a>.</p>
<p>I thought I’d take a look at how we can apply container queries to a very handy yet verbose CSS Grid declaration. The following code gives us a responsive grid where the number of columns is determined by the amount of available space, based on a minimum item width which is <em>either</em> 350px <em>or</em> 100%, whichever is smaller (using the <code>min()</code> function — so when the container size is below 350px the cards will be stacked vertically):</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.grid</span> <span class="token punctuation">{</span>
<span class="token property">grid-template-columns</span><span class="token punctuation">:</span> <span class="token function">repeat</span><span class="token punctuation">(</span>auto-fit<span class="token punctuation">,</span> <span class="token function">minmax</span><span class="token punctuation">(</span><span class="token function">min</span><span class="token punctuation">(</span>350px<span class="token punctuation">,</span> 100%<span class="token punctuation">)</span><span class="token punctuation">,</span> 1fr<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>This is a great snippet, but there is absolutely zero chance I’m going to remember how to write it every time. (I had to Google it just now.) But using container queries, the code feels less verbose and more intentional.</p>
<pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@container</span> <span class="token punctuation">(</span>width > 350px<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token selector">.grid</span> <span class="token punctuation">{</span>
<span class="token property">grid-template-columns</span><span class="token punctuation">:</span> <span class="token function">repeat</span><span class="token punctuation">(</span>auto-fit<span class="token punctuation">,</span> <span class="token function">minmax</span><span class="token punctuation">(</span>350px<span class="token punctuation">,</span> 1fr<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>Container queries and when/else statements have little to no browser support right now (none at all in the case of the latter), but given the current pace of progress in CSS, and the incredible people working hard on this stuff, I’m willing to bet that it won’t be too long before writing CSS for these kind of cases gets a whole lot easier.</p>
Creating 3D Characters in Three.js2021-10-10T00:00:00Zhttps://css-irl.info/creating-3d-characters-in-threejs/<p>In this tutorial we’ll talk through creating a three-dimensional character using Three.js, adding some simple but effective animation, and a generative colour palette.</p>
<p><a href="https://tympanus.net/codrops/2021/10/04/creating-3d-characters-in-three-js/">Read the full article on Codrops</a></p>
Simplifying Form Styles With accent-color2021-09-23T00:00:00Zhttps://css-irl.info/simplifying-form-styles-with-accent-color/<p>The new CSS accent-color property makes it quick and easy to roll out our brand colors to certain form inputs by leveraging user agent styles. In this article we’ll take a look at what it does and how to use it alongside color-scheme for simple, accessible checkboxes and radio buttons — and imagine how we might use it in the future.</p>
<p><a href="https://www.smashingmagazine.com/2021/09/simplifying-form-styles-accent-color/">Read the article</a></p>
Simpler Block Spacing in WordPress with :is() and :where()2021-09-20T00:00:00Zhttps://css-irl.info/simpler-block-styling-in-wordpress-with-is-and-where/<p>The <code>:is()</code> and <code>:where()</code> pseudo-selectors are relatively new additions to CSS, which allow us to target elements that meet the criteria in their parentheses. For example, using <code>:is()</code> we can target any <code>p</code>, <code>h2</code> or <code>ul</code> element with a class of <code>test</code>:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.test:is(p, h2, ul)</span> <span class="token punctuation">{</span>
<span class="token property">background</span><span class="token punctuation">:</span> yellow<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>That’s equivalent to writing:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">p.test,
h2.test,
ul.test</span> <span class="token punctuation">{</span>
<span class="token property">background</span><span class="token punctuation">:</span> yellow<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Conversely, we could target any <code>h2</code> that has any of the specified classes:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">h2:is(.heading-1, .heading-2)</span> <span class="token punctuation">{</span>
<span class="token property">background</span><span class="token punctuation">:</span> yellow<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>This is equivalent to:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">h2.heading-1,
h2.heading-2</span> <span class="token punctuation">{</span>
<span class="token property">background</span><span class="token punctuation">:</span> yellow<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>It works with complex selectors too. The following targets any <code>h2</code> that is a direct child of an element with a class of <code>has-red-heading</code>:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">h2:is(.has-red-heading > *)</span> <span class="token punctuation">{</span>
<span class="token property">color</span><span class="token punctuation">:</span> red<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p><code>:where()</code>, on the face of it, is pretty similar. Indeed, we can use it in the exact same way and it will have the same effect:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">h2:where(.heading-1, .heading-2)</span> <span class="token punctuation">{</span>
<span class="token property">background</span><span class="token punctuation">:</span> yellow<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>The difference is the selectors inside <code>:where()</code> have a specificity of zero, while the selector inside <code>:is()</code> contribute to the overall specificity. To quote directly from <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:where">MDN</a>:</p>
<blockquote>
<p>The difference between :where() and :is() is that :where() always has 0 specificity, whereas :is() takes on the specificity of the most specific selector in its arguments.</p>
</blockquote>
<p>Let’s look at how these can be useful in our CSS code.</p>
<h2>Better block styling</h2>
<p>At <a href="https://www.atomicsmash.co.uk/">Atomic Smash</a>, the web agency where I work, we specialise in building WordPress sites. Recent developments in WordPress have made it easier than ever before to deliver entirely flexible sites: the <a href="https://wordpress.org/gutenberg/">Gutenberg block editor</a> allows clients the full flexibility to add, remove or reorder blocks anywhere on the page. But it also comes with its challenges, not least of which is styling the many different block types to ensure suitable spacing between them.</p>
<h2>Gutenberg overview</h2>
<p>On a webpage built with the Gutenberg block editor, each “block” is a direct child of a single wrapper element on the page. Some are simple blocks, like headings and paragraphs, others are more complex components with classes appended. We can also create custom blocks to be used in much the same way.</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>entry-content<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token comment"><!--Heading and paragraph core blocks--></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span>Did dinosaurs really exist?<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>
Yes. Semper eget duis at tellus at urna condimentum mattis pellentesque.
Donec ac odio tempor orci dapibus ultrices in. In hac habitasse platea
dictumst quisque sagittis purus sit.
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span>
<span class="token comment"><!--More complex core block--></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>blockquote</span>
<span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>wp-block wp-block-quote<span class="token punctuation">"</span></span>
<span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>block-1cfd33a9-706c-4409-8a6e-d381bff67023<span class="token punctuation">"</span></span>
<span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>
Dinosaurs lived a really long time ago, but they can teach us a lot about
the present day
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>blockquote</span><span class="token punctuation">></span></span>
<span class="token comment"><!--Custom block--></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>as-accordion<span class="token punctuation">"</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>accordion-block_6065ca0dd7fa1<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token comment"><!--...block HTML--></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<h2>Block spacing</h2>
<p>Wordpress ships a bunch of default CSS for styling the space between Gutenberg blocks, but we usually want to override that based on our design. A handy way to do that is using the <a href="https://alistapart.com/article/axiomatic-css-and-lobotomized-owls/">lobotomised owl selector</a> for direct descendants of the content wrapper.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.entry-content > * + *</span> <span class="token punctuation">{</span>
<span class="token property">margin-top</span><span class="token punctuation">:</span> 1rem<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>This sets a top margin on each block when it follows another. It’s a good start, but some blocks generally need a bit more space, so we set a larger <code>margin-top</code> value for those blocks. Here’s how that looks using Sass, which is what we use at Atomic Smash:</p>
<pre class="language-scss"><code class="language-scss"><span class="token selector">.entry-content </span><span class="token punctuation">{</span>
<span class="token selector">> * + * </span><span class="token punctuation">{</span>
<span class="token property">margin-top</span><span class="token punctuation">:</span> 1rem<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">/* Any block followed by a h2, h3, h4, figure, blockquote or gallery block needs more space above */</span>
<span class="token selector">> * + h2,
> * + h3,
> * + h4,
> * + figure,
> * + blockquote,
> * + .wp-block-gallery </span><span class="token punctuation">{</span>
<span class="token property">margin-top</span><span class="token punctuation">:</span> 2rem<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">/* Any block that follows a figure, blockquote or gallery block needs more space above */</span>
<span class="token selector">> figure + *,
> blockquote + *,
> .wp-block-gallery + * </span><span class="token punctuation">{</span>
<span class="token property">margin-top</span><span class="token punctuation">:</span> 2rem<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>This is just a snapshot, and it might be that we need even more custom spacing on any given project, as well as considering different breakpoints.</p>
<p>We can refactor that to make it more concise using <code>:is()</code>:</p>
<pre class="language-scss"><code class="language-scss"><span class="token selector">.entry-content </span><span class="token punctuation">{</span>
<span class="token selector">> * + * </span><span class="token punctuation">{</span>
<span class="token property">margin-top</span><span class="token punctuation">:</span> 1rem<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token operator">></span> <span class="token operator">*</span> <span class="token operator">+</span> <span class="token punctuation">:</span><span class="token function">is</span><span class="token punctuation">(</span>h2<span class="token punctuation">,</span> h3<span class="token punctuation">,</span> h4<span class="token punctuation">,</span> figure<span class="token punctuation">,</span> blockquote<span class="token punctuation">,</span> .wp-block-gallery<span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token operator">></span> <span class="token punctuation">:</span><span class="token function">is</span><span class="token punctuation">(</span>figure<span class="token punctuation">,</span> blockquote<span class="token punctuation">,</span> .wp-block-gallery<span class="token punctuation">)</span> <span class="token selector">+ * </span><span class="token punctuation">{</span>
<span class="token property">margin-top</span><span class="token punctuation">:</span> 2rem<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p><code>:is()</code> is probably a better option than <code>:where()</code> in this case, as we want to increase the specificity and override the default Gutenberg styles.</p>
<p>Here’s a demo of it in action, alongside custom properties for scaling the vertical rhythm for different breakpoints, which can help keep our code even more concise:</p>
<p class="codepen" data-height="424" data-default-tab="html,result" data-slug-hash="OJgvrVN" data-user="michellebarker" style="height: 424px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/OJgvrVN">
</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
Developer Decisions For Building Flexible Components2021-09-09T00:00:00Zhttps://css-irl.info/developer-decisions-for-building-flexible-components/<p>One of the key skills of a front-end developer is to be able to take designs and turn them into code. These designs are often presented as static mock-ups, which visualize the “ideal” experience of browsing the website.</p>
<p>In the real world, content often differs vastly from the neat, perfectly fitting content presented in designs. Added to that, on the modern web, users have an ever-increasing range of options for how they access the sites we build.</p>
<p>In this article, we’ll walk through the process of taking a seemingly simple design for a text-and-media component and deciding how best to translate it into code, keeping in mind the needs of both users and content authors. We’re not going to delve into how to code it — rather, the factors that will determine our development decisions. We’ll consider the questions we need to ask (both ourselves and other stakeholders) at every step.</p>
<p><a href="https://www.smashingmagazine.com/2021/09/developer-decisions-building-flexible-components/">Read the full article</a></p>
Learning in the Open2021-09-04T00:00:00Zhttps://css-irl.info/learning-in-the-open/<p>Over the years I’ve tried out a lot of different ways of learning CSS and JS: books, online courses, articles, video tutorials… But for me, there’s really no substitute for learning by doing. In fact, I’d do so far as to say that this is probably the best way for <strong>anyone</strong> to learn front end coding (and perhaps most other disciplines too!). That’s not to say that the above resources aren’t useful — I still use of them frequently. But I consider them supplementary to actually learning “on the job”. Here I’d like to share what’s been successful for me when learning new front end skills, and some things that might help you too.</p>
<h2>Just start building</h2>
<p>Right now I’m trying to learn <a href="https://threejs.org/">ThreeJS</a>. I’ve read up on the basic concepts, dabbled in some tutorials, and started working my way through <a href="https://threejs-journey.xyz/">this excellent course</a> by Bruno Simon. But in any course, sticking with the prescribed exercises is only going to help you solve a given set of problems. Personally, I soon get overwhelmed by the amount of material there is to learn, and start to fear that I’ll never have what it takes to become an “expert”. That’s why I wholly advocate jumping out of the course and starting to build real projects as soon as you can. Once you start applying concepts to real problems, things click into place a lot more easily. They don’t have to be big projects either — just building something of your own design can be enough to provide the momentum to keep learning. The course, article or tutorial is always there for you to dip back in, and Google is at your fingertips to help with unexpected problems (or to lead you down new rabbit holes!).</p>
<h2>Show your workings</h2>
<p>The other thing I would advocate is sharing your work as you learn, even when (especially when) it’s not perfect. (I’ve written before about <a href="https://css-irl.info/imperfect">letting go of perfection</a>.) It’s easy to lose perspective and get caught up in the smallest details, thinking that your work is only worth something if it’s “finished”. But sharing your journey is a way to hold yourself accountable, and any engagement from a supportive community can be extra encouraging. It doesn’t just benefit you as a creator either: seeing work in progress can be really interesting for others, and might well encourage other people to share their work too. Some examples that spring to mind of people who do this are <a href="https://twitter.com/ilithya_rocks">Ilithya</a> (who often shares her work with shaders on Twitter) and <a href="https://twitter.com/mmatuzo">Manuel</a>, who is currently updating <a href="https://www.matuzo.at/">his website</a> in the open.</p>
<h2>Find a community</h2>
<p>Sharing your work on social media can be beneficial, but it can also be easy to fall into the trap of chasing likes. There are other ways to share work that can be just as beneficial, like meet-ups (on or offline) and Slack or Discord communities. Finding a space where you can feel confident sharing work, and be mutually supportive of each other, is invaluable. The feedback you get can also lead you in inspiring new directions!</p>
<h2>Quick wins</h2>
<p>As a working parent, I have a very limited amount of time in which to work on personal projects. One thing that really helps me is to set myself a goal of producing first draft of something (whether an article or a demo) within two hours. <a href="https://codepen.io/">Codepen</a> lends itself very well to this: I can spend two solid hours coding without any of the faff of project setup, and none of the pressure of a big project. As much as I like the idea of working on something bigger, I know that if I had to spend many weeks on the same thing, spread over evenings when I’m already tired from work, I’d soon lose motivation and start to feel defeatist. Maybe this works for you, or maybe not — plenty of people have the time and inclination to devote themselves to bigger projects, of course!</p>
<p>And because I’m in the mood for sharing, here’s a little ThreeJS demo I’ve worked on over the past couple of evenings.</p>
<p class="codepen" data-height="422" data-default-tab="result" data-slug-hash="zYzKKZY" data-user="michellebarker" style="height: 422px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/zYzKKZY">
ThreeJS glow figure with GSAP</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
New Length Debugging Tool in Chrome Canary2021-08-24T00:00:00Zhttps://css-irl.info/new-length-debugging-tool-in-chrome-canary/<p>A few weeks ago I blogged about <a href="https://css-irl.info/inspecting-sizes/">inspecting sizes</a> in the browser dev tools, lamenting the lack of a way to toggle between different unit types for length values:</p>
<blockquote>
<p>Currently, inspecting a colour value in Firefox or Chrome allows you to toggle between RGB, HSL and hex values by holding down the shift key on click. It would be great to see a similar thing for size values too. Being able to toggle between pixels, ems and rems (or even percentages) in the browser would be so useful!</p>
</blockquote>
<p>Well, it looks like the browser gods have answered my prayers! <a href="https://twitter.com/argyleink">Adam Argyle</a> on the Google Chrome team just shared this brand new feature that’s landed in Chrome Canary.</p>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">🆕 <length> Tool<br />in Chrome Canary today:<br /><br />✅ drag px, rem, em, vh, etc<br />✅ hold shift to adjust by 10<br />✅ hold alt/opt to adjust by .1<br />✅ change/explore unit types in a dropdown<br /><br />*much like design tools* 😎<br /><br />[video alt]<br />drag to adjust padding, gaps and widths! <a href="https://t.co/afthDT9l0Z">pic.twitter.com/afthDT9l0Z</a></p>— Adam Argyle (@argyleink) <a href="https://twitter.com/argyleink/status/1429869198197415939?ref_src=twsrc%5Etfw">August 23, 2021</a></blockquote> <script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>It looks super useful, and seems to work pretty well as far as I can see! It even allows you to toggle units you wouldn’t wouldn’t normally expect to use in CSS, like centimetres and millimetres (which are valid units, so I guess someone, somewhere is using them!). My only criticism is that (as with many other features in dev tools) the way to adjust by steps of 10 or 0.1 not <em>that</em> intuitive — without the tweet I wouldn’t even know. I also can’t be the only one who finds it a little fiddly to click and drag on the tiny area of the property value to scale it up or down. (You can just type the value you want though, or use the arrow keys to adjust.)</p>
<p>The unit switcher doesn’t currently appear to be keyboard accessible — but right now it’s effectively in beta, we’ll likely see some improvements by the time it makes its way into regular Chrome.</p>
<p>Adam <a href="https://twitter.com/argyleink/status/1429869254371790878?s=20">also asks</a>:</p>
<blockquote>
<p>What should happen when you change the unit?</p>
<ol>
<li>just the unit changes (current)</li>
<li>convert the length so it stays the same length but uses the new unit</li>
</ol>
</blockquote>
<p>From my perspective, option 2 is better for many of the situations where I’d use this, such as when a designer gives me a length in pixels and I quickly need to convert it to rems.</p>
<p>In summary, this is a great new feature to have in the browser and, while not yet perfect, pretty much does exactly what I’d hoped for!</p>
Masking One Element With Another2021-08-10T00:00:00Zhttps://css-irl.info/masking-one-element-with-another/<p>Somehow it escaped my knowledge until recently that the CSS <code>element()</code> function is supported in Firefox. In fact, it has been for <a href="https://caniuse.com/?search=element()">a good while</a> (although it still needs a prefix). “What is the element() function?”, I hear you cry. It’s a way to render a HTML element as an image value for use in our CSS. The example given on <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/element()">MDN</a> is using an image rendered on a <code><canvas></code> as a background image. That’s a pretty cool use case, although the CSS Paint API already allows us to create a paint worklet and use it in our CSS – check out <a href="https://css-tricks.com/exploring-the-css-paint-api-image-fragmentation-effect/">this tutorial</a> by <a href="https://www.temani-afif.com/">Temani Afif</a>. Perhaps that’s one reason why browser support seems to have stalled?</p>
<h2>element() as mask</h2>
<p>What I’m interested in is being able to use <em>any</em> element, particularly as a mask for another. An example is a text element, such as a heading. We can apply the element as a mask by referencing its ID:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">img</span> <span class="token punctuation">{</span>
<span class="token property">mask-image</span><span class="token punctuation">:</span> <span class="token function">-moz-element</span><span class="token punctuation">(</span>#mask<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>What’s cool about masking is it behaves in much the same way as a background. We can apply properties such as <code>mask-size</code>, <code>mask-repeat</code>, etc. to get the effect we want.</p>
<p>In this demo I’m using a heading (with a gradient applied) as an image mask.</p>
<p class="codepen" data-height="484" data-default-tab="html,result" data-slug-hash="QWvzzVM" data-user="michellebarker" style="height: 484px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/QWvzzVM">
Masking one element with another</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<h2>Accessibility</h2>
<p>There are some obvious issues here, not least around accessibility. What’s the best experience for someone using a screenreader? If the text mask is repeated several times, would it be better to have multiple elements? Or preferable to use <code>aria-hidden</code> to hide the element from a screenreader entirely, and describe the resulting effect in some other way (<code>aria-label</code> or <code>aria-labelledby</code>, for instance). An <code><h1></code> might not be the right choice here, anyway. The answers to these questions will invariably depend on context, differing from project to project.</p>
<p>The other issue is visually hiding the original element. As we’re using it as a mask, we might not want the original on display. You might be accustomed to using a utility class to visually hide elements, while ensuring they can still be read by screenreader software:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.visually-hidden</span> <span class="token punctuation">{</span>
<span class="token property">clip</span><span class="token punctuation">:</span> <span class="token function">rect</span><span class="token punctuation">(</span>0 0 0 0<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token function">inset</span><span class="token punctuation">(</span>50%<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">height</span><span class="token punctuation">:</span> 1px<span class="token punctuation">;</span>
<span class="token property">overflow</span><span class="token punctuation">:</span> hidden<span class="token punctuation">;</span>
<span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span>
<span class="token property">white-space</span><span class="token punctuation">:</span> nowrap<span class="token punctuation">;</span>
<span class="token property">width</span><span class="token punctuation">:</span> 1px<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>(Scott O'Hara has some <a href="https://www.scottohara.me/blog/2017/04/14/inclusively-hidden.html">more information</a> on this, and other methods for hiding content.)</p>
<p>This won’t work for this case, as it’ll render the element — and therefore our mask — invisible! In the demo, I’ve plumped for hiding it offscreen:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">h1</span> <span class="token punctuation">{</span>
<span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span>
<span class="token property">left</span><span class="token punctuation">:</span> -100%<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h2>Animation</h2>
<p>Another interesting use case could be using an animated element as a mask. However, the performance is pretty poor, as we can see from <a href="https://codepen.io/michellebarker/pen/ZEKwzQm">this demo</a>. But it’s much better when used as a transition:</p>
<p class="codepen" data-height="404" data-default-tab="html,result" data-slug-hash="xxdMKRK" data-user="michellebarker" style="height: 404px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/xxdMKRK">
Masking an element with an animated element</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<h2>Do we need element()?</h2>
<p>It’s worth pointing out that we can achieve a lot of these effects currently with SVG. But as someone who enjoys nothing better than tinkering with CSS, I’d love to see widespread support for <code>element()</code>, for creative coding if nothing else, and there are no doubt plenty of use cases I haven’t yet considered.</p>
<p>Vincent De Oliveira has an <a href="https://iamvdo.me/en/blog/css-element-function">excellent write-up</a> from a while back, including some creative examples. Some of the demos appear to be broken in modern browsers, but there are videos, and it’s great to get some inspiration for what could be possible.</p>
<p>There are currently several issues noted in the <a href="https://www.w3.org/TR/css-images-4/#element-notation">specification</a>, but it would be great to see some of these get resolved and the spec move forward.</p>
Is it Time to Ditch the Design Grid?2021-07-27T00:00:00Zhttps://css-irl.info/is-it-time-to-ditch-the-design-grid/<p>I came across this website, <a href="https://gridless.design/">Gridless Design</a> recently, and it immediately struck a chord. It’s something I’ve been thinking about for a long time — the way that the usual design process, where designers hand off static mockups based, so often, on a 12-column grid, is not fit for purpose. I know I’m far from the first person to think that way, but increasingly with the advancements in CSS layout in recent years, the design grid feels more like a hangover from print than ever. A solution to a problem that, on the web, we simply don’t have.</p>
<p>CSS layout features like flexbox and Grid enable us to build more flexible layouts that prioritise content. We talk about intrinsic and extrinsic sizing in CSS — sizing based on both content and context. The promised container queries specification will put even more power in the hands of developers. But it feels to me like the design process is still stuck in the past.</p>
<p>Designers will often prescribe that an element should span (for example) four out of 12 columns on a desktop screen, without the knowledge that their idea of “columns” on the web bears no relation to how the layout may actually be built. This isn’t a slight against any individual designer, but it feels like the industry as a whole has some catching up to do. There is a collective failure to think of components in terms of behaviour — how layouts will respond to different types of content, and atypical viewport sizes — as opposed to fixed breakpoints.</p>
<p>This means developers play a significant role in filling in the gaps. Even if you’re a developer who doesn’t think of yourself as a designer, you <em>are</em> a designer. It’s up to us to decide how a layout behaves at the “in between” sizes, the edge cases, or where the content differs from what’s provided in the design. This requires imagination and design thinking on the part of the developer.</p>
<p>Consider the following component, consisting of a heading, an image and paragraph of text. If we’re under strict instruction to follow a design grid, then as soon as we start to resize the screen the layout starts to look less pleasing.</p>
<figure>
<video width="100%" controls="" playsinline="">
<source src="https://css-irl.info/media/is-it-time-to-ditch-the-design-grid.mp4" type="video/mp4" />
</video>
<figcaption>Resizing the two layouts produces different result, depending on whether we force them to align to a grid</figcaption>
</figure>
<p>But given the freedom to build a more flexible layout, we can prioritise the content — using the intrinsic sizes of elements to dictate the distribution of space. In this video, the first component forces each element to stick to a prescribed grid. Almost immediately the layout starts to look broken as we resize the viewport, as each grid child gets proportionally narrower. The second component retains the width of the elements, but closes the gap between them as the screen narrows, resulting in an arguably more pleasing (not to mention legible) layout, despite not adhering to a strict grid.</p>
<p class="codepen" data-height="484" data-default-tab="result" data-slug-hash="KKmZmVx" data-user="michellebarker" style="height: 484px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/KKmZmVx">
Comparing rigid and flexible grids</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>Similarly, this quote from the Gridless Design site sums up nicely how a card layout can respond to the context, rather than fixed columns dictated by a design.</p>
<blockquote>
<p>The CSS grid syntax allows children to reflow into new columns as needed. Columns can be informed about their size by the content in their children or a fractional amount of available space. This technique is very powerful for card layouts, allowing cards to be as big as a specified column allows and wrapping to the next row when it might become too small.</p>
</blockquote>
<p>Many developers are embracing innovation in our CSS toolset. Andy Bell and Heydon Pickering created <a href="https://every-layout.dev/">Every Layout</a>, which helps developers learn to embrace the inherent flexibility and unknowability of the web, to build resilient layouts that work for variable content. It would be great to see more innovation in design tools to encourage designers to think in a similar way. Good communication during the design and development process can help too, although not everyone has that luxury.</p>
<p>But developers aren’t entirely without blame. Part of the problem too, is CSS frameworks. Tailwind, Bootstrap and plenty of others come with classes that make it trivial to build a layout that adheres to a simple grid. But if we want to build a flexible, robust, content-aware layout, you need to look beyond the frameworks and write some custom CSS. Paradoxically, where CSS Grid shines is not only in building layouts that adhere to a strict design grid, but in baking flexibility into our components. But the temptation to choose the quick and easy solution, rather than the <em>best</em> one, is hard to resist.</p>
<p>It feels like we need a big industry shake-up here. We shouldn’t be forcing content into a rigid design grid to the detriment of user experience. Users won’t notice if your design doesn’t align perfectly to a 12-column grid. They <em>will</em> notice if they can’t use the site you’ve built. In those cases, the grid is serving no one but the designer’s ego. That’s why I’m happy to see the Gridless Design site advocating ditching the grid altogether. It might sound like a radical proposition for some, but the article does a great job of explaining how other devices, such as gestalt principles of space, proximity and continuity can make for a good design without a grid.</p>
<p>The site provides a far better explanation than I have here, so <a href="https://gridless.design/">check it out</a>.</p>
Building A Dynamic Header With Intersection Observer2021-07-20T00:00:00Zhttps://css-irl.info/building-a-dynamic-header-with-intersection-observer/<p>Have you ever needed to build a UI where some component on the page needs to respond to elements as they’re scrolled to a certain threshold within the viewport — or perhaps in and out of the viewport itself?</p>
<p>This article for Smashing Magazine walks through building a page header that updates its colours as the user scrolls to different sections of the page.</p>
<p><a href="https://www.smashingmagazine.com/2021/07/dynamic-header-intersection-observer/#comments-dynamic-header-intersection-observer">Read the article</a></p>
Inspecting Sizes2021-07-10T00:00:00Zhttps://css-irl.info/inspecting-sizes/<p>I don’t know about you, but I’ve often had a designer, looking over a site I’m developing, say something like “Can you move that five pixels to the left?”. The problem is, most of the time I’m not using pixels. It’s frustrating that despite rem or em units generally being a <a href="https://www.24a11y.com/2019/pixels-vs-relative-units-in-css-why-its-still-a-big-deal/">better choice for the web</a>, design tools tend to favour pixels. It means that designers and developers are often speaking different languages when it comes to sizes.</p>
<p>Browser developer tools could step in to fill the gap here, and in some ways they already are. Currently, inspecting a colour value in Firefox or Chrome allows you to toggle between RGB, HSL and hex values by holding down the shift key on click. It would be great to see a similar thing for size values too. Being able to toggle between pixels, ems and rems (or even percentages) in the browser would be so useful!</p>
<h2>Toggling font size</h2>
<p>We can actually do this right now with fonts in Firefox dev tools, <a href="https://twitter.com/razvancaliman/status/1410904510999011330?s=20">as Razvan pointed out to me on Twitter</a>. The Fonts panel has a bunch of controls for inspecting fonts (and is especially useful for variable fonts), and one of those is being able to switch units for the font size. Selecting from the dropdown automatically recalculates the value in the corresponding units, which include rems, ems, pixels, percentages and vieport units.</p>
<figure>
<img src="https://css-irl.info/inspecting-sizes-01.jpg" alt="Screenshot of a website with the Fonts panel in Firefox dev tools open" />
<figcaption>The fonts panel in Firefox. Use the sliders to adjust the font size, and the dropdown to select the units.</figcaption>
</figure>
<p>Speaking personally, font size is probably where I need this most frequently, so it’s great we have it already. Chrome appears to have its own font editing tool in the works, but it doesn’t seem to be available yet — other than this tantalising glimpse from Addy Osmani, I haven’t seen anything more about it.</p>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">The new Font Editor Experiment in DevTools is great. Tweak typography dynamically with sliders (font size, line-height, spacing & weight)<br /><br />Thx <a href="https://twitter.com/EdgeDevTools?ref_src=twsrc%5Etfw">@EdgeDevTools</a> for bringing it to Chromium & <a href="https://twitter.com/ChromeDevTools?ref_src=twsrc%5Etfw">@ChromeDevTools</a> <a href="https://t.co/Ukko2yQlHh">pic.twitter.com/Ukko2yQlHh</a></p>— Addy Osmani (@addyosmani) <a href="https://twitter.com/addyosmani/status/1340755991743238146?ref_src=twsrc%5Etfw">December 20, 2020</a></blockquote> <script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>Still, it looks pretty interesting, and it’s great to see browser dev tools getting better and better at solving the needs of developers.</p>
<h2>Update!</h2>
<p>The Dev Tools Tips website has a little write up on how to <a href="https://devtoolstips.org/tips/en/convert-font-units/">enable the font inspector in Chrome</a>. It’s an experiemental feature, so you need to enable it in the dev tools settings.</p>
Detecting Hover-Capable Devices2021-06-30T00:00:00Zhttps://css-irl.info/detecting-hover-capable-devices/<p>With a greater proliferation of devices than ever before, we developers can no longer rely on viewport size as the factor that determines the styles we serve up to our website users. Up until fairly recently, we might have caught ourselves making assumptions based on the size of a device: that mobile devices would rely on touch input, for instance, while for larger screen sizes we might assume the majority of users would interact with our webpage using a mouse. We might provide different experiences with a media query:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.some-component</span> <span class="token punctuation">{</span>
<span class="token comment">/* Styles for touch devices */</span>
<span class="token punctuation">}</span>
<span class="token atrule"><span class="token rule">@media</span> screen <span class="token keyword">and</span> <span class="token punctuation">(</span><span class="token property">min-width</span><span class="token punctuation">:</span> 1024px<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token selector">.some-component</span> <span class="token punctuation">{</span>
<span class="token comment">/* Styles for hover-able devices */</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>This doesn’t really help us today. A decent iPad has a higher screen resolution than a low-end laptop. Or someone might be using their tablet as a secondary monitor — using it in this way with a mouse would mean they <em>would</em> be able to use their pointer to hover on elements. But the media query above doesn’t tell us anything about their input method.</p>
<p>Nowadays we need to be more sophisticated about how we detect how a user is browsing our site. Luckily some newer media queries serve this exact purpose.</p>
<h2>Level 5 Media Queries</h2>
<p>The <a href="https://drafts.csswg.org/mediaqueries-5">CSS Level 5 Media Queries specification</a> brings us all sorts of new media queries beyond the familiar ones for viewport size. One of these is the <a href="https://www.w3.org/TR/mediaqueries-5/#hover">hover feature</a>. This determines whether the user’s primary pointing device is capable of hovering on the page. The possible values are <code>hover</code> (which would be true for a device with a mouse, for instance) or <code>none</code> (which would be true for a tablet being used with touch input). We can use the media query like this:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.some-component</span> <span class="token punctuation">{</span>
<span class="token comment">/* Styles for touch devices */</span>
<span class="token punctuation">}</span>
<span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">hover</span><span class="token punctuation">:</span> hover<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token selector">.some-component</span> <span class="token punctuation">{</span>
<span class="token comment">/* Styles for hover-able devices */</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<aside>
<p><strong>Update:</strong> The <code>hover</code> and <code>pointer</code> features are actually part of the Level 4 Media Queries spec! It’s only recently that browser support has become near-universal.</p>
</aside>
<p class="codepen" data-height="360" data-default-tab="html,result" data-slug-hash="jOmNbgW" data-user="michellebarker" style="height: 360px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/jOmNbgW">
Hover media query</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>This works well in most browsers, but some versions of Android have a feature where a long press emulates hover, so the media query evaluates true. If we want to serve the same styles to those users as those of other touch devices, we need to query for a second media feature.</p>
<h3>Pointer</h3>
<p>The <code>pointer</code> feature tests whether the device has a pointer and the accuracy of the pointing device. Possible values are <code>coarse</code> (for a pointer such as a finger used on a touch screen), <code>fine</code> (a mouse, for example) or <code>none</code> (a device with no pointer).</p>
<p>Adding this to our media query successfully detects touch input on Android devices:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.some-component</span> <span class="token punctuation">{</span>
<span class="token comment">/* Styles for touch devices, including Android */</span>
<span class="token punctuation">}</span>
<span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">hover</span><span class="token punctuation">:</span> hover<span class="token punctuation">)</span> <span class="token keyword">and</span> <span class="token punctuation">(</span><span class="token property">pointer</span><span class="token punctuation">:</span> fine<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token selector">.some-component</span> <span class="token punctuation">{</span>
<span class="token comment">/* Styles for hover-able devices */</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p class="codepen" data-height="402" data-default-tab="html,result" data-slug-hash="NWjKxKP" data-user="michellebarker" style="height: 402px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/NWjKxKP">
Hover media query</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<h3>any-hover and any-pointer</h3>
<p>If that’s not quite enough, the <code>any-hover</code> and <code>any-pointer</code> media features enable us to test the capabilities of <em>any</em> of a devices input devices — not just the primary one. So if you have device that responds to both a mouse <em>and</em> touch input <code>any-hover: hover</code> would be true.</p>
<p>I haven’t needed to use these features yet, but the <a href="https://www.w3.org/TR/mediaqueries-5/#any-input">specification</a> includes several examples of how this can be used, with some different (and more complex) considerations.</p>
<h2>Javascript example</h2>
<p>I recently built a webpage where hovering on any one of several identical image links would bring up the name of the link, a bit like a tooltip. But users of touch devices wouldn’t see that tooltip. Instead, clicking on the image would take the user directly to the link URL, which might be a jarring experience as they wouldn’t know which page they were visiting. Instead, for touch devices, I decided to interrupt the click and show a button that the user could then press to go to the relevant URL. We can use the same media query to detect touch input in JS, using <code>matchMedia</code>:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> list <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'[data-list]'</span><span class="token punctuation">)</span>
<span class="token keyword">const</span> isHoverableDevice <span class="token operator">=</span> window<span class="token punctuation">.</span><span class="token function">matchMedia</span><span class="token punctuation">(</span>
<span class="token string">'(hover: hover) and (pointer: fine)'</span>
<span class="token punctuation">)</span>
<span class="token comment">/* Hide the block link initially */</span>
blockLink<span class="token punctuation">.</span>hidden <span class="token operator">=</span> <span class="token boolean">true</span>
list<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'click'</span><span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token comment">/* Do nothing if target is not a link, or device is hover-capable */</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>e<span class="token punctuation">.</span>target<span class="token punctuation">.</span>dataset<span class="token punctuation">.</span>link <span class="token operator">||</span> isHoverableDevice<span class="token punctuation">.</span>matches<span class="token punctuation">)</span> <span class="token keyword">return</span>
<span class="token comment">/* If touch device, show the block link */</span>
e<span class="token punctuation">.</span><span class="token function">preventDefault</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
blockLink<span class="token punctuation">.</span>hidden <span class="token operator">=</span> <span class="token boolean">false</span>
blockLink<span class="token punctuation">.</span>innerText <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">Visit </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>e<span class="token punctuation">.</span>target<span class="token punctuation">.</span>dataset<span class="token punctuation">.</span>link<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">’s page</span><span class="token template-punctuation string">`</span></span>
blockLink<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">'href'</span><span class="token punctuation">,</span> e<span class="token punctuation">.</span>target<span class="token punctuation">.</span>href<span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<p>A quick demo:</p>
<p class="codepen" data-height="422" data-default-tab="html,result" data-slug-hash="rNmBxpJ" data-user="michellebarker" style="height: 422px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/rNmBxpJ">
Touch/hover tooltip</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<h2>Accessibility</h2>
<p>Depending on the UI, we might want to give assistive technologies a helping hand here, by using ARIA attributes to announce the button when the change occurs, or moving the user’s focus to the button. <a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions">This example</a> from MDN includes a demonstration of how to use ARIA live regions to announce dynamic changes to an element.</p>
<h2>Browser support</h2>
<p>The great news is you can use these media queries right now, as they’re supported in all modern browsers. Now you can provide better experiences for users of <em>all</em> devices!</p>
Video: Dev Roulette Live2021-06-27T00:00:00Zhttps://css-irl.info/video-dev-roulette-live/<p>Last week I had the honour of participating in <a href="https://www.youtube.com/channel/UCmAXY1OelfjilsGMHGmVdIQ">Dev Roulette Live</a> — a new video series co-ordinated by <a href="https://twitter.com/5t3ph">Stephanie Eckles</a>, where developers are paired up for live conversation about their specialist subjects. The added twist is a roulette wheel, spun at different points to choose topics at random. One of my favourite things to do is talk about CSS, so this was super fun to be a part of!</p>
<p>I was also really excited to meet <a href="https://twitter.com/bramus">Bramus</a>, who I was paired up with. Bramus has a <a href="https://www.bram.us/">fantastic blog</a> that covers loads of useful tips and bleeding-edge CSS features. In the conversation we talked about our favourite CSS tricks, creativity, why we do what we do, and much more. Enjoy the video!</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/_qRyPamGyEc" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
Trigonometry in CSS and JS: A Series2021-06-11T00:00:00Zhttps://css-irl.info/trigonometry-in-css-and-js-a-series/<p>While working on some demos earlier this year, I started to get really interested in trigonometry. Having left the subject alone since school, I initially started brushing up on my knowledge to solve a specific problem. But I was soon drawn in by how a grasp of trigonometry opens up the possibilities for creative coding!</p>
<p>If the subject sounds intimidating, this article series I wrote for Codrops might be for you. In it we walk through the principles of trigonometry and some creative applications in CSS and JS. Starting from the basics, we gain an understanding of the various trigonometric functions, and move through to using them in our CSS code, before graduating to drawing and animating shapes with the Canvas API. I learnt a lot while writing these articles, and I hope you will too. Maths and creative coding need not be intimidating!</p>
<h2>Read the articles</h2>
<ol>
<li><a href="https://tympanus.net/codrops/2021/06/01/trigonometry-in-css-and-javascript-introduction-to-trigonometry/">Introduction to Trigonometry</a></li>
<li><a href="https://tympanus.net/codrops/2021/06/02/trigonometry-in-css-and-javascript-getting-creative-with-trigonometric-functions/">Getting Creative with Trigonometric Functions</a></li>
<li><a href="https://tympanus.net/codrops/2021/06/04/trigonometry-in-css-and-javascript-beyond-triangles/">Beyond Triangles</a></li>
</ol>
Creating Generative SVG Characters2021-05-08T00:00:00Zhttps://css-irl.info/creating-generative-svg-characters/<figure>
<img src="https://css-irl.info/creating-generative-svg-characters.jpg" alt="A grid of six multicoloured SVG people" />
</figure>
<p>I came across <a href="https://twitter.com/georgedoescode">George Francis’</a> work recently, and am completely in love with his generative SVG characters — especially the googly eyes! What I particularly love about these is that they underline how generative art doesn’t have to be serious — it can be playful, whimsical and fun.</p>
<p>George also has a <a href="https://georgefrancis.dev/">brand new website</a>, which includes some exceptionally well-written tutorials on how you can get started with generative SVG art. Reading <a href="https://georgefrancis.dev/writing/generative-svg-blob-characters/">this one on generative SVG blob characters</a>, it struck me as an ideal approach for a little generative project I had in mind.</p>
<h2>The project</h2>
<p>Recently I helped my 5-year-old son with a craft project to create some character cards. He’s not particularly keen on drawing, so getting him engaged in creative activities requires a bit of thought. We had a selection of coloured card (a palette of five or six colours). I drew shapes (circles, squares or triangles), and he would cut them out and stick them on a background, for the head and body of each character. Then he would draw on the eyes, mouth and limbs with a felt-tip pen.</p>
<figure>
<img src="https://css-irl.info/creating-generative-svg-characters-01.jpg" alt="A grid of six multicoloured paper people" />
</figure>
<p>The premise was simple, and lends itself perfectly to generative art. Take some fixed parameters (a limited colour palette, a fixed background size, a small set of shapes), and add in a few random elements — in this case the cutting, sticking and drawing skills of a 5-year-old! It resulted in a lovely set of artworks, which I thought would be really fun to try and produce generatively.</p>
<h2>The generative version</h2>
<p>Until I ready George’s tutorial, I had the vague idea that I would use SVG to create the generative characters. George’s art inspired me to get started, and the tutorial provided loads of useful tips that made the process <em>so</em> much easier than if I’d attempted it on my own.</p>
<h3>SVG.js</h3>
<p><a href="https://svgjs.dev/">SVG.js</a> is a useful JS library for creating and positioning SVG elements, such as circles, rectangles, lines and polygons. It saves tons of time compared to creating an element and appending it to the DOM manually. I’ll definitely be reaching for this again in the future.</p>
<h3>A handy random function</h3>
<p>George’s <code>random()</code> function is super handy as it allows you to specify whether you want an integer or a floating point value:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">function</span> <span class="token function">random</span><span class="token punctuation">(</span><span class="token parameter">min<span class="token punctuation">,</span> max<span class="token punctuation">,</span> float <span class="token operator">=</span> <span class="token boolean">false</span></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> val <span class="token operator">=</span> Math<span class="token punctuation">.</span><span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token punctuation">(</span>max <span class="token operator">-</span> min<span class="token punctuation">)</span> <span class="token operator">+</span> min
<span class="token keyword">if</span> <span class="token punctuation">(</span>float<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">return</span> val
<span class="token punctuation">}</span>
<span class="token keyword">return</span> Math<span class="token punctuation">.</span><span class="token function">floor</span><span class="token punctuation">(</span>val<span class="token punctuation">)</span>
<span class="token punctuation">}</span></code></pre>
<p>I ended up using this all over the place to generate randomised properties of the characters.</p>
<h3>Drawing the polygon</h3>
<p>The heads and bodies of the characters are (vaguely) circular. To get the <em>x</em> and <em>y</em> path co-ordinates we can use some trigonometric functions (<code>Math.cos()</code> and <code>Math.sin()</code>) to plot these. Using George’s code, here’s how we would plot a regular polygon with 16 points:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> size <span class="token operator">=</span> <span class="token number">300</span>
<span class="token keyword">const</span> xPosition <span class="token operator">=</span> <span class="token number">100</span>
<span class="token keyword">const</span> yPosition <span class="token operator">=</span> <span class="token number">100</span>
<span class="token keyword">const</span> numPoints <span class="token operator">=</span> <span class="token number">16</span>
<span class="token comment">// step used to place each point at equal distances</span>
<span class="token keyword">const</span> angleStep <span class="token operator">=</span> <span class="token punctuation">(</span>Math<span class="token punctuation">.</span><span class="token constant">PI</span> <span class="token operator">*</span> <span class="token number">2</span><span class="token punctuation">)</span> <span class="token operator">/</span> numPoints
<span class="token comment">// keep track of our points</span>
<span class="token keyword">const</span> points <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>
<span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> i <span class="token operator">=</span> <span class="token number">1</span><span class="token punctuation">;</span> i <span class="token operator"><=</span> numPoints<span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment">// x & y coordinates of the current point</span>
<span class="token keyword">const</span> x <span class="token operator">=</span> xPosition <span class="token operator">+</span> Math<span class="token punctuation">.</span><span class="token function">cos</span><span class="token punctuation">(</span>i <span class="token operator">*</span> angleStep<span class="token punctuation">)</span> <span class="token operator">*</span> size
<span class="token keyword">const</span> y <span class="token operator">=</span> xPosition <span class="token operator">+</span> Math<span class="token punctuation">.</span><span class="token function">sin</span><span class="token punctuation">(</span>i <span class="token operator">*</span> angleStep<span class="token punctuation">)</span> <span class="token operator">*</span> size
<span class="token comment">// push the point to the points array</span>
points<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token punctuation">{</span> x<span class="token punctuation">,</span> y <span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span></code></pre>
<p>Try changing the variables in the following demo to change the polygon’s position, size and number of points.</p>
<p class="codepen" data-height="383" data-theme-id="dark" data-default-tab="js,result" data-user="michellebarker" data-slug-hash="MWpYRLd" style="height: 383px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="SVG polygon">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/MWpYRLd">
SVG polygon</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>In the tutorial, George use the <code>random()</code> function to add an element of variation to the polygon points, resulting in irregular shapes.</p>
<h3>Instantiating with a class</h3>
<p>I use <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes">classes</a> a lot in my JS at work to create reusable component behaviours that can be instantiated at will. But not so much in my creative coding projects. Creating a class, as George does in this tutorial, is a great approach here. We can just instantiate it anywhere we want to add one of our generative characters.</p>
<h2>Adaptations</h2>
<p>While George’s code provided a great foundation to work with, I ended up making a few adaptations for my needs.</p>
<h3>Colours</h3>
<p>In my code, instead of fully randomising the colours, I wanted to have a limited palette from which to choose — just like having a selection of coloured card. So rather than generating colours randomly, I used a function to shuffle the array of colours and pick the first three. This ensures we get three different colour, rather than using the same colour twice within each character.</p>
<h3>Shapes</h3>
<p>George uses a <code>spline()</code> function to draw a smooth line the polygon shapes. This is great for creating the “blob” shapes that George uses, but for my characters I wanted to keep the jagged edges reminiscent of the real-life cut-out figures, so I didn’t end up using this function.</p>
<p>I also mixed up the shapes a bit to better reflect the real-life variations, by adding a trapezium shape into the mix — so each character will have a body shape based on either a regular polygon or a trapezium.</p>
<p>Here’s the full demo:</p>
<p class="codepen" data-height="439" data-theme-id="dark" data-default-tab="result" data-user="michellebarker" data-slug-hash="ZEeYPbK" style="height: 439px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Generative SVG paper people">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/ZEeYPbK">
Generative SVG paper people</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>Of course, you can read the <a href="https://georgefrancis.dev/writing/generative-svg-blob-characters/">full article</a>, which I thoroughly recommend if you want to get started with your own generative SVG project.</p>
Toggling CSS Custom Properties with Radio Buttons2021-05-04T00:00:00Zhttps://css-irl.info/toggling-css-custom-properties-with-radio-buttons/<p>As part of a <a href="https://tympanus.net/codrops/2021/05/04/dynamic-css-masks-with-custom-properties-and-gsap/">recent article published on Codrops</a>, I made some demos that allowed the user to toggle between three different values for a clip path using radio buttons. As with so many things these days, I found myself reaching for <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/--*">custom properties</a>! Let’s take a look at why custom properties are great for this.</p>
<h2>Common approaches</h2>
<p>Let’s look at one way we could approach this <em>without</em> custom properties. We could use Javascript to detect when a user interacts with a radio button and append a class to the <code><figure></code> accordingly:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> clippedElement <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'.clipped-element'</span><span class="token punctuation">)</span>
<span class="token keyword">const</span> controls <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'.controls'</span><span class="token punctuation">)</span>
<span class="token keyword">let</span> currentClass <span class="token operator">=</span> <span class="token string">'circle'</span>
<span class="token keyword">const</span> <span class="token function-variable function">onChange</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>e<span class="token punctuation">.</span>target<span class="token punctuation">.</span>value <span class="token operator">||</span> <span class="token operator">!</span>e<span class="token punctuation">.</span>target<span class="token punctuation">.</span>checked<span class="token punctuation">)</span> <span class="token keyword">return</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>clippedElement<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">contains</span><span class="token punctuation">(</span>currentClass<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
clippedElement<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">replace</span><span class="token punctuation">(</span>currentClass<span class="token punctuation">,</span> e<span class="token punctuation">.</span>target<span class="token punctuation">.</span>value<span class="token punctuation">)</span>
<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
clippedElement<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>e<span class="token punctuation">.</span>target<span class="token punctuation">.</span>value<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
currentClass <span class="token operator">=</span> e<span class="token punctuation">.</span>target<span class="token punctuation">.</span>value
<span class="token punctuation">}</span>
controls<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'change'</span><span class="token punctuation">,</span> onChange<span class="token punctuation">)</span></code></pre>
<p>Then it’s a matter of defining our clip-path values for each class within our CSS:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.clipped-element</span> <span class="token punctuation">{</span>
<span class="token property">-webkit-clip-path</span><span class="token punctuation">:</span> <span class="token function">circle</span><span class="token punctuation">(</span>25% at 70%<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token function">circle</span><span class="token punctuation">(</span>25% at 50%<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.clipped-element.polygon</span> <span class="token punctuation">{</span>
<span class="token property">-webkit-clip-path</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>50% 0%...<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>50% 0%...<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.clipped-element.svg</span> <span class="token punctuation">{</span>
<span class="token property">-webkit-clip-path</span><span class="token punctuation">:</span> <span class="token function">path</span><span class="token punctuation">(</span><span class="token string">'M202.2...'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token function">path</span><span class="token punctuation">(</span><span class="token string">'M202.2...'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>I’ve shortened the values of the last two here for brevity and readability, but <a href="https://codepen.io/michellebarker/pen/mdRZjdR">this demo</a> has the full code.</p>
<p>Another option is to set the <code>style</code> attribute in JS, but I prefer the CSS way, as it feels cleaner to me. <code>clip-path</code> requires a prefix in some browsers, so setting this in CSS seems more maintainable — but it’s a matter of personal preference.</p>
<p>While there’s nothing <em>wrong</em> with these approaches, let’s see what the alternative looks like with custom properties.</p>
<h2>Toggling the custom property</h2>
<p>In our HTML we have three radio buttons with values of <em>circle</em>, <em>polygon</em> and <em>svg</em> respectively:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>form</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>controls<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>fieldset</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>legend</span><span class="token punctuation">></span></span>Switch clip path values<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>legend</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>radio<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>shape<span class="token punctuation">"</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>r_circle<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>circle<span class="token punctuation">"</span></span> <span class="token attr-name">checked</span> <span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>r_circle<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Circle<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>radio<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>shape<span class="token punctuation">"</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>r_polygon<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>polygon<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>r_polygon<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Polygon<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>input</span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>radio<span class="token punctuation">"</span></span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>shape<span class="token punctuation">"</span></span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>r_svg<span class="token punctuation">"</span></span> <span class="token attr-name">value</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>svg<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>label</span> <span class="token attr-name">for</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>r_svg<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>SVG path<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>label</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>fieldset</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>form</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>clipped-element<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>We can assign a custom property for each value in CSS (again, the SVG path and polygon values are truncated here for brevity):</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.clipped-element</span> <span class="token punctuation">{</span>
<span class="token property">--circle</span><span class="token punctuation">:</span> <span class="token function">circle</span><span class="token punctuation">(</span>25% at 50%<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">--polygon</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>50% 0%...<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">--svg</span><span class="token punctuation">:</span> <span class="token function">path</span><span class="token punctuation">(</span><span class="token string">'M202.2...'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Additionally, we can assign a custom property of <code>--clip</code> a value for our clip path. That means that every time we change the <code>--clip</code> value, both the prefixed property value <em>and</em> the regular property value will be updated. (I’ve written about this in a previous article, <a href="https://css-irl.info/7-uses-for-css-custom-properties/">7 Uses For Custom Properties</a>.)</p>
<p>We’ll give it an initial value corresponding to our first radio button option, using the <code>--circle</code> custom property:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.clipped-element</span> <span class="token punctuation">{</span>
<span class="token property">--circle</span><span class="token punctuation">:</span> <span class="token function">circle</span><span class="token punctuation">(</span>25% at 70%<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">--polygon</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>50% 0%...<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">--svg</span><span class="token punctuation">:</span> <span class="token function">path</span><span class="token punctuation">(</span><span class="token string">'M202.2...'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">--clip</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--circle<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">-webkit-clip-path</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--clip<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--clip<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Then, in our <code>onChange</code> event handler, we update the value of <code>--clip</code> with the custom property corresponding to the selected radio input. We can use <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals">template literals</a> to do this:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> <span class="token function-variable function">onChange</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>e<span class="token punctuation">.</span>target<span class="token punctuation">.</span>value <span class="token operator">||</span> <span class="token operator">!</span>e<span class="token punctuation">.</span>target<span class="token punctuation">.</span>checked<span class="token punctuation">)</span> <span class="token keyword">return</span>
clippedElement<span class="token punctuation">.</span>style<span class="token punctuation">.</span><span class="token function">setProperty</span><span class="token punctuation">(</span><span class="token string">'--clip'</span><span class="token punctuation">,</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">var(--</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>e<span class="token punctuation">.</span>target<span class="token punctuation">.</span>value<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">)</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span>
<span class="token punctuation">}</span></code></pre>
<p>We achieve the same result, and both our CSS and JS code is much more concise.</p>
<p>The full demo:</p>
<p class="codepen" data-height="453" data-theme-id="dark" data-default-tab="result" data-user="michellebarker" data-slug-hash="bGgXyYp" style="height: 453px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Clip path toggle with custom properties">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/bGgXyYp">
Clip path toggle with custom properties</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>Incidentally, CSS Tricks has recently published a <a href="https://css-tricks.com/a-complete-guide-to-custom-properties/">complete guide to custom properties</a>, featuring some great tips and examples of ways to use them.</p>
Dynamic CSS Masks with Custom Properties and GSAP2021-05-04T00:00:00Zhttps://css-irl.info/dynamic-css-masks-with-custom-properties-and-gsap/<p>Learn how to animate CSS masks based on the cursor position using GSAP and custom properties for a unique spotlight effect.</p>
<p><a href="https://tympanus.net/codrops/2021/05/04/dynamic-css-masks-with-custom-properties-and-gsap/">Read the article ➡️</a></p>
Development Budgets2021-04-15T00:00:00Zhttps://css-irl.info/development-budgets/<p>Personally, I find that one of the hardest things to do as a developer is estimate how long a piece of work will take. At <a href="https://atomicsmash.co.uk/">Atomic Smash</a> we’ve developed some processes over time that enable us to estimate a lot of common development tasks with <em>some</em> degree of accuracy:</p>
<ul>
<li>We invest a lot of time in planning.</li>
<li>We break projects down granularly, so that we’re mostly estimating individual tasks rather that large bodies of work.</li>
<li>We record our time, so that hopefully when another project comes around that needs similar functionality, we’ll be able to estimate even more accurately, with greater awareness of any potential stumbling blocks.</li>
</ul>
<p>In addition, we have an in-house boilerplate, which is added to over time, to make the development process faster, as well as easier to estimate.</p>
<p>But in almost every project, there is always some piece of functionality that is difficult to plan and budget for accurately. On the front end, it’s nearly always a Javascript-heavy area of the site. In part, this is down to those parts being more complex in their requirements, and difficult to break down into granular tasks. These parts might also require a lot more research and experimentation: sometimes I have no idea how I’m going to build something until I’ve had some time to conduct the necessary research — and in those cases I have to be honest when it comes to my estimates and give a ballpark range with the caveat that it could vary wildly. I try to err on the side of caution with those type of estimates and give higher numbers, even when it’s not what the project manager wants to hear.</p>
<p>Another reson for the difficulty with estimating these JS-heavy tasks is the time that needs to be spent testing and debugging. Often a component that looks simple on paper throws up all sorts of edge cases when users interact with it in ways we don’t anticipate. In most cases it falls to the developer to consider all these possible scenarios. If we’re doing our jobs properly, we should be building accessible components and asking ourselves questions as we go: Can users who rely on keyboard navigation focus on links within this accordion? How does a screenreader user know there is additional content available by clicking on this icon button? How is this tooltip accessed when using a touch device, as opposed to a mouse? How do we accommodate users with motion sensitivity in a site with lots of animation?</p>
<p>However simple a component looks at the design stage, when I’m estimating I try to add 60–80% extra to budget for making it fully accessible and doing the necessary testing. How a component functions in its most basic form is just the tip of the iceberg: It’s relatively easy to build a tab component (for instance), but to build an <em>accessible</em> tab component? That’s considerably harder — but should be non-negotiable.</p>
<p>That’s not to say my own estimates are always 100% accurate, and I don’t have all the answers on the best ways to estimate accurately. But recording front end estimates for each component in a design allows me to better consider the overall development budget (in terms of number of hours), and plan with the design team accordingly. If the designer or the client comes to me with a feature request on a project, I can assess its impact on the development budget and frame it as a trade-off. Many of our clients have fixed budgets, so adding a feature that requires a lot of work could mean having to simplify several other features in order to free up the necessary time. Sometimes this discussion results in a simplification of the design, and often I can work with the designer to suggest ways to make the feature more attainable within the budget.</p>
<p>Another advantage with taking a granular approach is that at any time in the project you can see how many estimated development hours are remaining. It becomes much easier for the project manager to see if a project is running behind and needs extra resources.</p>
<p>Estimates can never be perfect: there will inevitably be tasks that end up being more difficult and time-consuming than budgeted, in ways that cannot be foreseen. But much like managing personal finances, budgeting development work is a skill that’s worth working on, and can greatly reduce stress on a project.</p>
Paper Snowflakes: Combining Clipping and Masking in CSS2021-04-07T00:00:00Zhttps://css-irl.info/paper-snowflakes-combining-clipping-and-masking-in-css/<figure>
<img src="https://css-irl.info/paper-snowflakes-01.jpg" width="1600" height="900" alt="Screenshot of three CSS paper snowflakes on black background" />
</figure>
<p>Just after Christmas I made a fun little <a href="https://codepen.io/michellebarker/pen/YzGEJoX">Codepen demo</a> recreating realistic-looking paper snowflakes in CSS, inspired by our homemade decorations! Christmas might be a distant memory, but there were plenty of learnings from this process that might be interesting to share.</p>
<h2>Folded paper effects</h2>
<p><a href="https://twitter.com/lynnandtonic">Lynn Fisher</a> published a great <a href="https://lynnandtonic.com/thoughts/entries/case-study-2020-refresh/">walkthrough of her wonderful personal site redesign</a>, which features some very cool paper-fold effects. (Resize your browser to see them in action!) She’s also built a number of demos that feature variations on those kind of effects, like <a href="https://codepen.io/lynnandtonic/pen/PoZpjOr">this awesome folded poster</a>, where she creates the fold effects using layered background gradients. Lynn’s work was a big inspiration behind using gradients to make my paper snowflakes look like they were cut out of real paper. I used <code>conic-gradient</code> and <code>radial-gradient</code> backgrounds to attain a subtle light-and-shadow effect. <code>conic-gradient</code> has only fairly recently gained widespread browser support, but it’s great fun to play around with, as it allows for some striking and creative effects in CSS.</p>
<h2>Clipped segments</h2>
<p>My initial idea was to try to use a single element for each snowflake, which would likely be possible with some <em>very</em> clever use of gradients. But early on I decided a multi-element solution would suit my goals better. I wanted to retain the integrity of the snowflake being clipped from a single folded segment, and what better way to do that than using <code>clip-path</code>? Each segment of the snowflake is a single element, clipped identically and absolute-positioned inside a container. The segment has a width and height of 50% (<em>Fig 01</em>). I could have used an element of 100% width and height, but this would mean that when clipping (using a percentage-based <code>clip-path</code> polygon I would constantly have to remember that my polygon values must not exceed 50%, and I felt like that would be a little irritating.</p>
<figure>
<img src="https://css-irl.info/paper-snowflakes-02.svg" width="1600" height="900" alt="A square element absolute positioned in the top right quadrant of a container" loading="lazy" />
<figcaption><em>Fig 01</em></figcaption>
</figure>
<p>Let’s look at a single segment as an example. Initially, instead of calculating the exact <code>clip-path</code> value, which would require some maths (we’ll come back to that later!), I used custom properties and <code>mask-image</code> to snip out a segment. Masking is a little like clipping, but instead of cutting out an element with clean lines we can use images with alpha transparency, or even gradients, to hide or reveal areas. I like to think of <code>clip-path</code> as like clipping with scissors, whereas <code>mask-image</code> is a bit like rubbing away areas of a charcoal-covered surface with an eraser, to reveal the image underneath. It allows for more subtle gradation.</p>
<h3>Custom properties</h3>
<p>First of all, I set a custom property for the number of segments. Then I used that to calculate a second custom property, for the angle of a single segment:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.snowflake</span> <span class="token punctuation">{</span>
<span class="token property">--totalSegments</span><span class="token punctuation">:</span> 16<span class="token punctuation">;</span>
<span class="token property">--segment</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span>360deg / <span class="token function">var</span><span class="token punctuation">(</span>--totalSegments<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>I used this segment custom property for the angle of the conic gradient I wanted to use as a mask:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.snowflake</span> <span class="token punctuation">{</span>
<span class="token property">--totalSegments</span><span class="token punctuation">:</span> 16<span class="token punctuation">;</span>
<span class="token property">--segment</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span>360deg / <span class="token function">var</span><span class="token punctuation">(</span>--totalSegments<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">--mask</span><span class="token punctuation">:</span> <span class="token function">conic-gradient</span><span class="token punctuation">(</span>
from 0deg at 0 100%<span class="token punctuation">,</span>
<span class="token function">rgba</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 1<span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token function">rgba</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 1<span class="token punctuation">)</span> <span class="token function">var</span><span class="token punctuation">(</span>--segment<span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token function">rgba</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 0<span class="token punctuation">)</span> <span class="token function">var</span><span class="token punctuation">(</span>--segment<span class="token punctuation">)</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>This gives us a conic gradient radiating from the bottom left of the element, with clean edges, much like using a clip-path — except I’m hardly having to do any maths at all. This is the mask we’ll use on each segment of the snowflake, and results in a triangular shape. (<em>Fig 02</em>)</p>
<figure>
<img src="https://css-irl.info/paper-snowflakes-03.svg" width="1600" height="900" alt="The triangular element as a result of the applied mask" loading="lazy" />
<figcaption><em>Fig 02</em></figcaption>
</figure>
<h3>Applying the mask</h3>
<p>I’m setting a custom property for the mask, as the <code>mask-image</code> property needs to be prefixed in a number of browsers — so instead of having to duplicate the entire property value we can can simply use it as a variable:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.segment</span> <span class="token punctuation">{</span>
<span class="token property">--webkit-mask-image</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--mask<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">mask-image</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--mask<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h3>Clipping a segment</h3>
<p>Now we have a segment, which represents a square of paper repeatedly folded diagonally into sixteenths. We can start clipping out sections, as if we’re cutting with scissors, by using <code>clip-path</code>. I did this entirely by eye in the original demo, tweaking the values until I was happy with them. I used percentage values for the clip path points because I want the snowflake to be able to scale.</p>
<p>At the end of it I ended up with a polygon where the values on the right hand side were just outside of the mask boundary. (<em>Fig 03</em>)</p>
<figure>
<img src="https://css-irl.info/paper-snowflakes-04.svg" width="1600" height="900" alt="The clipped element" loading="lazy" />
<figcaption><em>Fig 03</em></figcaption>
</figure>
<p>(Pro-tip: If you increase the alpha value of the transparent part of the gradient mask while you’re working on your clip path, you can see exactly where your path falls behind the mask.) It might now be clearer why we’re using a mask – it means we don’t have to worry about being so precise with our polygon points and calculating those with trigonometry. A little later on we’ll look at trigonometry in more detail, and why that might be a better solution.</p>
<p>Our <code>clip-path</code> polygon ends up being quite a lot of values to keep track of. We can manage those a bit more easily with custom properties. I used custom properties to effectively keep track of each section I was clipping. So (almost) every custom property has three pairs of values. For example, a triangle clipped from the left side could be represented by these custom properties in the clip-path polygon:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.snowflake</span> <span class="token punctuation">{</span>
<span class="token property">--l1</span><span class="token punctuation">:</span> 0 80%<span class="token punctuation">,</span> 5% 75%<span class="token punctuation">,</span> 0 60%<span class="token punctuation">;</span>
<span class="token comment">/* Triplets of values are used in the clip-path polygon to form "triangles" */</span>
<span class="token property">--path</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>
0 0<span class="token punctuation">,</span>
<span class="token function">var</span><span class="token punctuation">(</span>--r1<span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token function">var</span><span class="token punctuation">(</span>--r2<span class="token punctuation">)</span><span class="token punctuation">,</span>
100% <span class="token function">var</span><span class="token punctuation">(</span>--y<span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token function">var</span><span class="token punctuation">(</span>--center<span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token function">var</span><span class="token punctuation">(</span>--l1<span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token function">var</span><span class="token punctuation">(</span>--l2<span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token function">var</span><span class="token punctuation">(</span>--l3<span class="token punctuation">)</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<figure>
<img src="https://css-irl.info/paper-snowflakes-04a.svg" width="1600" height="900" alt="Three clip-path points highlighted with their corresponding values" loading="lazy" />
<figcaption><em>Fig 04</em></figcaption>
</figure>
<p>That way it’s much clearer which section the values we’re tweaking apply to. This was especially helpful when making subsequent variations of the snowflakes: I only had to tweak the custom properties, rather than the entire clip path.</p>
<h3>Transforms</h3>
<p>Now we can position each segment by rotating it around a point. We can set the <code>transform-origin</code> value to <code>bottom left</code>, or <code>0 100%</code>, so it will be rotate from (you guessed it) the bottom left. Again, custom properties can help us here. I’m assigning each segment a custom property that corresponds to its index. I do this for quite a lot of projects with a generative aspect, and I often prefer to do it in the HTML. (It’s especially easy if you use a templating language.) It means that if you add or remove elements, you don’t need to keep updating your CSS file.</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>snowflake<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>segment<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">--i</span><span class="token punctuation">:</span> 1</span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>segment<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">--i</span><span class="token punctuation">:</span> 2</span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>segment<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">--i</span><span class="token punctuation">:</span> 3</span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>segment<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">--i</span><span class="token punctuation">:</span> 4</span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>segment<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">--i</span><span class="token punctuation">:</span> 5</span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>segment<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">--i</span><span class="token punctuation">:</span> 6</span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>segment<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">--i</span><span class="token punctuation">:</span> 7</span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>segment<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">--i</span><span class="token punctuation">:</span> 8</span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>segment<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">--i</span><span class="token punctuation">:</span> 9</span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>segment<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">--i</span><span class="token punctuation">:</span> 10</span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>segment<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">--i</span><span class="token punctuation">:</span> 11</span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>segment<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">--i</span><span class="token punctuation">:</span> 12</span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>segment<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">--i</span><span class="token punctuation">:</span> 13</span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>segment<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">--i</span><span class="token punctuation">:</span> 14</span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>segment<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">--i</span><span class="token punctuation">:</span> 15</span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>segment<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">--i</span><span class="token punctuation">:</span> 16</span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>Now we can transform each segment based on its index, in a single line of code:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.segment</span> <span class="token punctuation">{</span>
<span class="token property">transform-origin</span><span class="token punctuation">:</span> 0 100%<span class="token punctuation">;</span>
<span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">rotate</span><span class="token punctuation">(</span><span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--segment<span class="token punctuation">)</span> * <span class="token function">var</span><span class="token punctuation">(</span>--i<span class="token punctuation">,</span> 1<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<figure>
<img src="https://css-irl.info/paper-snowflakes-05.svg" width="1600" height="900" alt="Identical segments rotated around a point" loading="lazy" />
<figcaption><em>Fig 05</em></figcaption>
</figure>
<p>We’re not quite done however, because every <em>other</em> segment should be fully rotated on the <em>y</em> axis—so it’s a mirror image—just like when you unfold a real paper snowflake:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.segment:nth-child(even)</span> <span class="token punctuation">{</span>
<span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">rotateY</span><span class="token punctuation">(</span>180deg<span class="token punctuation">)</span> <span class="token function">rotate</span><span class="token punctuation">(</span><span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--segment<span class="token punctuation">)</span> * <span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--i<span class="token punctuation">,</span> 1<span class="token punctuation">)</span> - 1<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<figure>
<img src="https://css-irl.info/paper-snowflakes-06.svg" width="1600" height="900" alt="The resulting snowflake shape after rotation applied" />
<figcaption><em>Fig 06</em></figcaption>
</figure>
<p>The conic-gradient background is also applied to each segment, so for the even segments we’ll reverse it. We’re using a conic gradient layered with a radial gradient, so if we set the conic gradient as a custom property we can make our code a little more concise by avoiding duplicating the radial gradient value for the even segments:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.snowflake</span> <span class="token punctuation">{</span>
<span class="token property">--bg</span><span class="token punctuation">:</span> <span class="token function">repeating-conic-gradient</span><span class="token punctuation">(</span>
from 0deg at 0 100%<span class="token punctuation">,</span>
white<span class="token punctuation">,</span>
<span class="token function">rgba</span><span class="token punctuation">(</span>200<span class="token punctuation">,</span> 200<span class="token punctuation">,</span> 200<span class="token punctuation">,</span> 1<span class="token punctuation">)</span> <span class="token function">var</span><span class="token punctuation">(</span>--segment<span class="token punctuation">)</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.segment</span> <span class="token punctuation">{</span>
<span class="token property">background</span><span class="token punctuation">:</span> <span class="token function">radial-gradient</span><span class="token punctuation">(</span>
circle at 0% 100%<span class="token punctuation">,</span>
<span class="token function">rgba</span><span class="token punctuation">(</span>200<span class="token punctuation">,</span> 200<span class="token punctuation">,</span> 200<span class="token punctuation">,</span> 1<span class="token punctuation">)</span><span class="token punctuation">,</span>
transparent 40%
<span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token function">var</span><span class="token punctuation">(</span>--bg<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.snowflake:nth-child(even)</span> <span class="token punctuation">{</span>
<span class="token property">--bg</span><span class="token punctuation">:</span> <span class="token function">repeating-conic-gradient</span><span class="token punctuation">(</span>
from 0deg at 0 100%<span class="token punctuation">,</span>
<span class="token function">rgba</span><span class="token punctuation">(</span>200<span class="token punctuation">,</span> 200<span class="token punctuation">,</span> 200<span class="token punctuation">,</span> 1<span class="token punctuation">)</span><span class="token punctuation">,</span>
white <span class="token function">var</span><span class="token punctuation">(</span>--segment<span class="token punctuation">)</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h2>Drop shadow</h2>
<p>As a finishing touch, we can add a subtle drop shadow to the entire snowflake to make it look even more realistic, using the <code>drop-shadow</code> filter:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.snowflake</span> <span class="token punctuation">{</span>
<span class="token property">filter</span><span class="token punctuation">:</span> <span class="token function">drop-shadow</span><span class="token punctuation">(</span>1rem 1rem 1rem <span class="token function">rgba</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 0.9<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>This is one of the advantages of clipping each segment with <code>clip-path</code>, instead of relying on gradient trickery, where “transparent” parts might not really be transparent – our drop shadow behaves like the snowflake really is cut out of paper, including the clipped areas inside.</p>
<h2>A better outcome with trigonometry</h2>
<p>Using a gradient mask combined with <code>clip-path</code> worked well for this demo, but it felt a little like cheating. Although I am happy with the end result, it means I largely relied on trial-and-error to determine my clip-path polygon points. This didn’t feel 100% satisfactory to me. I knew that trigonometry had to be the solution, so in a subsequent demo I decided to use it to calculate my clip-path co-ordinates precisely.</p>
<p>For my next demo, I made an interactive version of the paper snowflake. Users can drag the handles to alter the clip path, creating a unique snowflake each time. The demo uses Greensock’s <a href="https://greensock.com/docs/v3/Plugins/Draggable">Draggable</a> plugin. It was my first time playing around with this, and although I’m using it in a fairly basic way, it seems super helpful for building interfaces where users can move things around.</p>
<p class="codepen" data-height="474" data-theme-id="dark" data-default-tab="result" data-user="michellebarker" data-slug-hash="VwKdONM" style="height: 474px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Snowflakes with clip-path trigonometry">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/VwKdONM">
Snowflakes with clip-path trigonometry</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>I won’t dive into a step-by-step walk-through of how I built this, but in a future post I’ll share how trigonometry helped me here, and some different ways to use it in CSS and Javascript.</p>
Quick Tip: Style Pseudo-elements with Javascript Using Custom Properties2021-03-28T00:00:00Zhttps://css-irl.info/quick-tip-style-pseudo-elements-with-javascript-using-custom-properties/<p>In Javascript we have a few ways of selecting elements, but we can’t directly target <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-elements">pseudo-elements</a>. Something like this, for instance, won’t work, and will return <code>null</code>:</p>
<pre class="language-js"><code class="language-js">document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'.my-element::after'</span><span class="token punctuation">)</span></code></pre>
<p>That can make it tricky to apply styles to pseudo-elements with JS. If we want to dynamically calculate the height of an element and apply it with JS, we can do it like so:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> element <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'.my-element'</span><span class="token punctuation">)</span>
element<span class="token punctuation">.</span>style<span class="token punctuation">.</span>height <span class="token operator">=</span> <span class="token function">someFunctionToCalculateHeight</span><span class="token punctuation">(</span><span class="token punctuation">)</span></code></pre>
<p>But how do we do this with a pseudo-element? Luckily, CSS custom properties can help.</p>
<p>We can instead assign a custom property for the height in our CSS:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.my-element::after</span> <span class="token punctuation">{</span>
<span class="token property">height</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--height<span class="token punctuation">,</span> 0<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Here I’m providing a default (or fallback) value of 0 for the custom property. It’s a good idea to provide a default if we’re relying on the custom property being defined in JS (if we want it to be anything other than the property’s default value).</p>
<p>Then we can set the value in JS:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> element <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'.my-element'</span><span class="token punctuation">)</span>
element<span class="token punctuation">.</span>style<span class="token punctuation">.</span><span class="token function">setProperty</span><span class="token punctuation">(</span><span class="token string">'--height'</span><span class="token punctuation">,</span> <span class="token function">someFunctionToCalculateHeight</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span></code></pre>
<p>Here’s a simple demo (and yes, I know the JS in this example is unecessary as it could easily be achieved with CSS alone — it’s merely to illustrate the concept!):</p>
<p class="codepen" data-height="405" data-theme-id="dark" data-default-tab="js,result" data-user="michellebarker" data-slug-hash="LYxZJZp" style="height: 405px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Style pseudo-element with custom property">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/LYxZJZp">
Style pseudo-element with custom property</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
Animating Underlines2021-03-24T00:00:00Zhttps://css-irl.info/animating-underlines/<p>I recently gave my <a href="https://michellebarker.co.uk/">personal website</a> a makeover and included a few cool little CSS tricks. Over the next few posts I’ll share some of these. The first one is all about underlines. Hover over any of the links in the body copy on the site and you’ll notice the underline transitions downwards. On the web it’s pretty common to seeing animated underline effects using pseudo-elements and/or borders. Try hovering on the examples in this demo.</p>
<p class="codepen" data-height="355" data-theme-id="dark" data-default-tab="result" data-user="michellebarker" data-slug-hash="MWJajOb" style="height: 355px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Animated with pseudo elements">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/MWJajOb">
Animated with pseudo elements</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>These work great for single, short lines of text (such as navigation links), but not for multi-line text. We can animate an underline on multi-line text with clever use of linear gradients, along with <code>background-size</code> and <code>background-position</code>. Here’s an example, and <a href="https://nickymeuleman.netlify.app/blog/css-animated-wrapping-underline">a great article about how to implement this technique</a>.</p>
<p class="codepen" data-height="407" data-theme-id="dark" data-default-tab="css,result" data-user="michellebarker" data-slug-hash="BapoQNj" style="height: 407px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Animated underline with background">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/BapoQNj">
Animated underline with background</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>This approach has its limitations, however: it requires the text to be an inline element, so it works well for anchor links in a paragraph of text, say. But if you wanted an animated underline on a heading element you’d likely need to modify the markup to add a <code><span></code> inside the element, which is not always an option.</p>
<h2>“Real” underlines</h2>
<p>With some of the newer <code>text-decoration-</code> properties, we can animate the <em>actual</em> underlines — far superior to just letting our underlines blink in and out of existence on hover. Any by animating real underlines, we can retain the nice feature that most browsers give us, where the underline skips the text’s descenders (the default for the <code>text-decoration-skip-ink</code> property).</p>
<p>For the most basic example, we can implement a fade-in effect. We can’t animate the opacity of a text underline, but we <em>can</em> animate it from transparent to our desired colour. Firstly we set the <code>text-decoration-style</code> property to <code>underline</code>. Here I’m using the shorthand <code>text-decoration</code> to specify the <code>text-decoration-thickness</code> and <code>text-decoration-color</code> at the same time. We can set the colour to a transparent value. Then on hover, we can transition it to an opaque value:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">a</span> <span class="token punctuation">{</span>
<span class="token property">text-decoration</span><span class="token punctuation">:</span> underline 0.15em <span class="token function">rgba</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 0<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">transition</span><span class="token punctuation">:</span> text-decoration-color 300ms<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">a:hover</span> <span class="token punctuation">{</span>
<span class="token property">text-decoration-color</span><span class="token punctuation">:</span> <span class="token function">rgba</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 1<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p class="codepen" data-height="393" data-theme-id="dark" data-default-tab="css,result" data-user="michellebarker" data-slug-hash="VwPvPMp" style="height: 393px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Simple fade underline animation">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/VwPvPMp">
Simple fade underline animation</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>That’s better than the default, but fairly basic. Even better, we can transition the <code>text-underline-offset</code> property, which is relatively new but has widespread browser support. Here’s how I’m using it on my own site:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">a</span> <span class="token punctuation">{</span>
<span class="token property">text-decoration</span><span class="token punctuation">:</span> underline 0.15em <span class="token function">rgba</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 1<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">text-underline-offset</span><span class="token punctuation">:</span> 0.2em<span class="token punctuation">;</span>
<span class="token property">transition</span><span class="token punctuation">:</span> text-decoration-color 300ms<span class="token punctuation">,</span> text-underline-offset 300ms<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">a:hover</span> <span class="token punctuation">{</span>
<span class="token property">text-decoration-color</span><span class="token punctuation">:</span> <span class="token function">rgba</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 255<span class="token punctuation">,</span> 1<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">text-underline-offset</span><span class="token punctuation">:</span> 0.4em<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h3>Units</h3>
<p>As a side note, I like using <em>em</em> units for these values because they are relative to the font size, which means if we have text that is larger or smaller the underline will scale proportionally.</p>
<h3>Browser support</h3>
<p>The above works great...in Firefox. Right now, no other browsers appear to support transitioning or animating <code>text-underline-offset</code>. (The same applies to <code>text-decoration-thickness</code>, which can also allow for some interesting effects.) But luckily, there is an alternative approach to animating these properties...</p>
<h2>Houdini to the rescue</h2>
<p>Without getting into the fine technical details, <a href="https://developer.mozilla.org/en-US/docs/Web/Houdini">CSS Houdini</a> is a set of low-level APIs that expose parts of the browser’s CSS rendering engine to developers. It allows us to register a custom property and animate it with CSS. Previously developers needed to register the property in Javascript, but now it’s possibly to do it <em>entirely</em> with CSS, using <code>@property</code>. Una has a <a href="https://web.dev/at-property/">great article explaining exactly how to use it</a> and some of the things that are possible.</p>
<p>For our purposes, we can register a property called <code>--offset</code>, which we’ll use for the <code>text-underline-offset</code> value.</p>
<pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@property</span> --offset</span> <span class="token punctuation">{</span>
<span class="token property">syntax</span><span class="token punctuation">:</span> <span class="token string">'<length>'</span><span class="token punctuation">;</span>
<span class="token property">inherits</span><span class="token punctuation">:</span> false<span class="token punctuation">;</span>
<span class="token property">initial-value</span><span class="token punctuation">:</span> 0px<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>It’s important to set an initial value, otherwise it won’t work. For some reason ems don’t seem to work as an initial value, although I’m not sure why.</p>
<p>Then, instead of transitioning <code>text-underline-offset</code>, we transition the custom property itself:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">a</span> <span class="token punctuation">{</span>
<span class="token property">transition</span><span class="token punctuation">:</span> --offset 300ms<span class="token punctuation">,</span> text-decoration-color 300ms<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">a:hover,
a:focus</span> <span class="token punctuation">{</span>
<span class="token property">--offset</span><span class="token punctuation">:</span> 0.4em<span class="token punctuation">;</span>
<span class="token property">text-decoration-color</span><span class="token punctuation">:</span> <span class="token function">rgba</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 255<span class="token punctuation">,</span> 1<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h2>Testing for support</h2>
<p>Unfortunately, transitioning custom properties with Houdini is not supported in Firefox or Safari – so we’re back to the previous problem of having a solution with limited browser support! But never fear, we <em>can</em> implement a cross-browser solution — with belt and braces!</p>
<p>We can use a feature query to detect whether a browser does not support Houdini (this query relates to the Paint API). For browsers that don’t support Houdini, we’ll instead transition the <code>text-underline-offset</code> property — which, luckily, works in Firefox and Safari!</p>
<pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@supports</span> <span class="token keyword">not</span> <span class="token punctuation">(</span><span class="token property">background</span><span class="token punctuation">:</span> <span class="token function">paint</span><span class="token punctuation">(</span>something<span class="token punctuation">)</span><span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token selector">a</span> <span class="token punctuation">{</span>
<span class="token property">transition</span><span class="token punctuation">:</span> text-underline-offset 400ms<span class="token punctuation">,</span> text-decoration-color 400ms<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">a:hover,
a:focus</span> <span class="token punctuation">{</span>
<span class="token property">text-underline-offset</span><span class="token punctuation">:</span> 0.4em<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>Here’s the solution in full:</p>
<p class="codepen" data-height="441" data-theme-id="dark" data-default-tab="css,result" data-user="michellebarker" data-slug-hash="mdRyYVP" style="height: 441px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Underlines (Chrome solution with Houdini)">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/mdRyYVP">
Underlines (Chrome solution with Houdini)</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
Debugging Media Queries: A Dev Tools Wish List2021-03-02T00:00:00Zhttps://css-irl.info/debugging-media-queries-a-dev-tools-wish-list/<p>The <a href="https://www.w3.org/TR/mediaqueries-5/">Level 5 Media Queries specification</a> brings us the ability to detect a whole load of different user preferences from within our CSS file, and serve up styles accordingly. Current support for the various media queries within the specification is mixed, but there are some that already have widespread support and are safe to use right now. For instance, we can detect whether a user has their system preferences set to dark mode using the <code>prefers-color-scheme</code> media query:</p>
<pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">prefers-color-scheme</span><span class="token punctuation">:</span> dark<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token comment">/* Styles */</span>
<span class="token punctuation">}</span></code></pre>
<p>Or detect a preference for reduced motion — useful for making our sites more inclusive by restricting animations for users who suffer from motion sickness or vestibular disorders and change their system preferences accordingly.</p>
<pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">prefers-reduced-motion</span><span class="token punctuation">:</span> reduce<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token comment">/* Styles */</span>
<span class="token punctuation">}</span></code></pre>
<p>On a Mac you can enable reduced motion by going to <em>System Preferences > Accessibility</em> and selecting the “Reduced motion” option.</p>
<p>Here’s a lesser-know media query I like a lot:</p>
<pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">hover</span><span class="token punctuation">:</span> hover<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token comment">/* Styles */</span>
<span class="token punctuation">}</span></code></pre>
<p>This detects the ability of the user’s primary pointing device to hover on the page. I’m using this in my current website redesign, to serve up slightly different styles for non-mouse users, or those browsing with touch-screen devices.</p>
<h2>Debugging media queries</h2>
<p>With an increasing number of user preferences to detect, we don’t just have to consider that our website will look different at different screen sizes. It’s conceivable that how it look to the user might depend on a whole host of preferences.</p>
<p>Rather than having to test our sites by repeatedly switching our settings at a system level, it would be great if browser dev tools had the ability to toggle various user preferences on and off. Firefox recently added a dark mode toggle to their dev tools. You can find it under the ‘Rules’ panel in the inspector — try toggling light and dark mode on this site to see it in action!</p>
<figure>
<img src="https://css-irl.info/debugging-media-queries-a-dev-tools-wish-list-01.jpg" alt="Screenshot of the dark and light mode buttons within the Firefox inspector" />
</figure>
<p>I’d love to see a similar thing for the above features too, and even for <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@supports">feature queries</a> — which allow us to test for browser support within our CSS file, and provide fallback styles for browsers that don’t support the feature in question. It would certainly make it much easier to test all the possible appearances a website could have! The fact is, there are a whole lot of different ways people can browse the web, and there’s a good chance the way your site looks could vary widely. A solid starting point is to build with resilient, semantic HTML, and go from there.</p>
Favourite Things 2: New CSS Features, JS Libraries and More2021-02-15T00:00:00Zhttps://css-irl.info/favourite-things-02/<p>Here’s a short round-up of some of the web technologies and resources that are getting me excited right now. Having started with the best intentions to write one of these posts regular, it’s been several months since the first one.</p>
<h2>Aspect Ratio</h2>
<p>Chrome (and Edge by extension) just shipped support for the new CSS <code>aspect-ratio</code> property! CSS Tricks has a breakdown of what this is and how to use it. Once this has widespread support, it’ll render <a href="https://css-tricks.com/aspect-ratio-boxes/">padding hacks</a> a thing of the past. The syntax couldn’t be simpler — here’s how we could create an element with 4:3 aspect ratio:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.element</span> <span class="token punctuation">{</span>
<span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>
<span class="token property">aspect-ratio</span><span class="token punctuation">:</span> 4 / 3<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Google’s developer blog features a <a href="https://web.dev/aspect-ratio/">full breakdown of how to use it</a> it in conjunction with <code>object-fit</code> for aspect ratio images.</p>
<h2>Container queries on the horizon</h2>
<p>Another thing that’ll soon be making our lives easier is the much-anticipated specification for container queries. Media queries have long bestowed on developers the ability to style elements differently depending on viewport width, but one thing that’s eluded us is styling based on the size of a parent element. Think of a grid of cards which might not take up the full page width. In a layout with a sidebar we might want to implement a two-column layout, but without a sidebar we might want to show rows of three, or use a different style altogether. Ethan Marcotte wrote a more thorough <a href="https://ethanmarcotte.com/wrote/on-container-queries/">explanation of why container queries are useful</a> a few of years ago.</p>
<p>Flexbox and CSS Grid have made it easier to create intrinsically responsive layouts without the need for media queries in the recent years, but even so, few developers would disagree that container queries - styling dependent on the size of a containing element - would be very handy. A practical implementation turned out to be anything but straightforward, and many had pretty much given up hope. But recently the discussion <a href="https://github.com/w3c/csswg-drafts/issues/5796">was revived</a>, and it looks like we might get our wish after all! It’s still too early to say when we can expect to be able to start using container queries, but hey, it gives us something to look forward to.</p>
<h2>Draggable</h2>
<p>I’ve written before about how much I love animating with <a href="https://greensock.com/">Greensock</a> (GSAP). Recently I had fun playing around with the <a href="https://greensock.com/docs/v3/Plugins/Draggable">Draggable</a> plugin to build this paper snowflake maker - drag the handles to cut out a unique paper snowflake. The snowflake segments are made using CSS clip-path, and dragging the handles updates the polygon path value. I made heavy use of custom properties here too.</p>
<p class="codepen" data-height="485" data-theme-id="dark" data-default-tab="result" data-user="michellebarker" data-slug-hash="VwKdONM" style="height: 485px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Snowflakes with clip-path trigonometry">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/VwKdONM">
Snowflakes with clip-path trigonometry</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<h2>Course: Three.js Journey</h2>
<figure>
<img src="https://css-irl.info/favourite-things-02-01.jpg" alt="Screen shot of the Three.js Journey landing page" />
</figure>
<p>You might have seen Bruno Simon’s <a href="https://bruno-simon.com/">amazing portfolio site</a> – a visual feast where you drive a car through a 3D landscape — or come across some of his other work on the web. These immersive experiences are built with <a href="https://threejs.org/">Three.js</a>, and Bruno has recently released a comprehensive Three.js video course, <a href="https://threejs-journey.xyz/">Three.js Journey</a> — complete with its own spectacular landing page! The course takes you from the basics right through to advanced techniques, and there’s plenty to learn along the way, even if you have some experience with Three.js. It’s great value too!</p>
<p>I’m enjoying working my way through it. Here’s a little bug animation I made using what I’ve learnt so far.</p>
<p class="codepen" data-height="441" data-theme-id="dark" data-default-tab="result" data-user="michellebarker" data-slug-hash="JjbEjrX" style="height: 441px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Three JS grub">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/JjbEjrX">
Three JS grub</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<h2>Create App</h2>
<p>One of the common gripes with modern front end development is the time it can take just to install and configure all the necessary tooling and frameworks. Whether your build tool of choice is Webpack, Parcel or Snowpack, <a href="https://createapp.dev/">Create App</a> helps you get a project up-and-running quickly and easily. Select your options for styling, linting, JS frameworks and more to generate a project boilerplate. I can definitely see myself using this for starting future projects.</p>
<h2>Newsletters</h2>
<p>Here are a couple of newsletters that always bring joy to my inbox.</p>
<h3>ViewBox</h3>
<figure>
<img src="https://css-irl.info/favourite-things-02-02.jpg" alt="Screen shot of the Viewbox homepage" />
</figure>
<p>This fabulous newsletter by <a href="https://twitter.com/cassiecodes">Cassie Evans</a> and <a href="https://twitter.com/Mamboleoo">Louis Hoebregts</a> focuses on one SVG-related topic per issue, and features gorgeous demos and an SVG challenge. I also really love the header animation and can’t stop replaying it.</p>
<p><a href="https://viewbox.club/">Subscribe now</a></p>
<h3>Front End Horse</h3>
<figure>
<img src="https://css-irl.info/favourite-things-02-03.jpg" alt="Screen shot of the Front End Horse homepage" />
</figure>
<p>The <a href="https://frontend.horse/">Frontend Horse</a> website has recently undergone a redesign, with some gorgeous illustrations. Each issue of the newsletter features an in-depth breakdown of a creative demo from the likes of <a href="https://twitter.com/cobra_winfrey">Adam Kuhn</a>, <a href="https://twitter.com/andybarefoot">Andy Barefoot</a>, <a href="https://twitter.com/ilithya_rocks">Ilithya</a> and more. I was honoured that Alex chose to feature my <a href="https://codepen.io/michellebarker/pen/dyMQYYz">Weird Fishes</a> demo in a recent issue!</p>
<p><a href="https://frontend.horse/archives">Subscribe now</a></p>
Finding an Element’s Nearest Relative Positioned Ancestor2021-02-09T00:00:00Zhttps://css-irl.info/finding-an-elements-nearest-relative-positioned-ancestor/<p><strong>This article was updated on 15 March 2021.</strong></p>
<p>Have you ever been faced with a CSS positioning dilemma where an element with <code>position: absolute</code> isn’t being positioned as you’d expect? Setting absolute positioning on an element will position it in relation to its nearest ancestor that has its position set to something other than <code>static</code> (the default).</p>
<figure>
<img src="https://css-irl.info/finding-an-elements-nearest-relative-positioned-ancestor-01.jpg" alt="Two examples showing position of a purple element when the parent has relative positioning versus when another ancestor has it" />
</figure>
<p>In the above image, the absolute-positioned element is positioned with the same CSS in both examples:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.absolute</span> <span class="token punctuation">{</span>
<span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span>
<span class="token property">top</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>
<span class="token property">left</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>But it ends up in a different place in each example. This is because in the first example its parent (the pink element) has <code>position: relative</code>, whereas in the second it’s another ancestor that has relative positioning (the grey element).</p>
<p class="codepen" data-height="461" data-theme-id="dark" data-default-tab="css,result" data-user="michellebarker" data-slug-hash="ZEBOZdj" style="height: 461px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Relative and absolute positioning">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/ZEBOZdj">
Relative and absolute positioning</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>It’s worth noting that if no ancestor is positioned, an element with <code>position: absolute</code> will be placed in relation to the <code><body></code>.</p>
<p>In a relatively simple example like this one, a quick examination of the CSS makes it easy to determine which ancestor has relative positioning, and we can adjust our styles accordingly if they’re not having the desired effect. But sometimes, in more complex codebases (especially with a lot of nested elements), finding which ancestor of an element has relative positioning can be a little trickier. This most commonly happens to me when building complex headers with full-width dropdown submenus: I generally need to position them in relation to the entire header, but somewhere I’ve inadvertently set <code>position: relative</code> on some other element, which breaks the desired behaviour.</p>
<p>Trawling through all that code can be time-consuming, but happily there is an easier way to find the nearest positioned parent in Javascript — which we can do right in the browser console.</p>
<p>In Chrome and Firefox, if we open the Console tab in the developer tools, we can get the currently selected element by typing <code>$0</code>. Then we can use the <code>offsetParent</code> object property to find the closest ancestor to that element that has its position set to something other than <code>static</code>. Try selecting an element and typing this into the console:</p>
<pre class="language-js"><code class="language-js">$0<span class="token punctuation">.</span>offsetParent</code></pre>
<p>This won’t actually tell us the position value (whether it’s <code>relative</code>, <code>fixed</code> or something else). But we can use <code>getComputedStyle</code> to find out the value of the element’s <code>position</code> property:</p>
<pre class="language-js"><code class="language-js"><span class="token function">getComputedStyle</span><span class="token punctuation">(</span>$0<span class="token punctuation">.</span>offsetParent<span class="token punctuation">)</span><span class="token punctuation">.</span>position</code></pre>
<p>By typing <code>$_</code> into the console we can retrieve the most recently evaluated expression as a variable, which can make this quicker too:</p>
<pre class="language-js"><code class="language-js">$0<span class="token punctuation">.</span>offsetParent</code></pre>
<p>shows us the element. Then:</p>
<pre class="language-js"><code class="language-js"><span class="token function">getComputedStyle</span><span class="token punctuation">(</span>$_<span class="token punctuation">)</span><span class="token punctuation">.</span>position</code></pre>
<p>retrieves its position value.</p>
<p>There are a couple of caveats: <code>offsetParent</code> will return null if the element has its postion set to <code>fixed</code> or itself or its parent has <code>display: none</code>. (<a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetParent">See MDN docs</a> for details.)</p>
<p>In my experience <code>position: relative</code> is most commonly used for offsetting a descendent (as in the example I’ve used), but it’s worth bearing in mind that <code>fixed</code> or <code>sticky</code> values will also permit the behaviour — but perhaps those elements are a little easier to spot in the browser!</p>
My Typical Day2021-02-05T00:00:00Zhttps://css-irl.info/my-typical-day/<p>I’ve been reading quite a few blog posts in a kind of unofficial series started by <a href="http://cdevroe.com/2021/01/07/my-typical-day/">Colin Devroe</a>, who kicked things off by writing about his typical day and inviting others to do the same. It’s like looking in through a little window on other people’s worlds, which I love – and right now it feels like a great way of connecting with others, which is more important than ever. Sometimes I pick up some productivity tips too!</p>
<p><a href="https://matthiasott.com/notes/my-typical-day">Matthias Ott</a> recently wrote about his day, and tagged me and a few others, so here’s my day. There are quite a few similarities — we both have children, so both of our schedules are largely dictated by them! — but hopefully enough differences to keep things interesting. If you haven’t already, I urge you to check out the thread and read some of the other posts too.</p>
<ul>
<li>
<p><strong>7:00</strong> – I wake up, occasionally with my alarm, but more often than not by being prodded awake by my 4–year-old, who usually finds his way into our bed during the night. Sometimes I go for a run. I try to run three times a week, and feel much better starting work in the morning knowing that I’ve already done my daily exercise. If it’s not a run day, I get up and make breakfast for me and my son.</p>
</li>
<li>
<p><strong>8:00—9:00</strong> – I get my son ready for his day of lessons, and myself ready for work. Normally I would walk him to school, but right now the entire country is in lockdown, so we’re homeschooling. It’s important for him to keep a sense of routine, so I make sure he’s dressed and ready before my husband comes down to take over. He doesn’t like to sit still for long, so we often do a CBeebies workout together, or end up playing dinosaurs.</p>
</li>
<li>
<p><strong>9:00</strong> – Log on for work with a coffee and say “hi” to the rest of the <a href="https://atomicsmash.co.uk/">Atomic Smash</a> team. I often have a meeting or two in the morning, and a good couple of hours to work on a project before lunch.</p>
</li>
<li>
<p><strong>12:00</strong> – I make myself a quick lunch, then take my son for a bike ride (weather permitting). Our lockdown project has been teaching him to ride without stabilisers, which he’s just about mastered. When he’s at school I instead have half and hour on the drum kit before going back to work.</p>
</li>
<li>
<p><strong>13:00</strong> – Back to work for the rest of the afternoon. This is usually fairly uninterrupted, but we have regular development collaboration sessions every few weeks, where we discuss improvements to our working practices and share any cool new stuff we’ve learned. We regularly get together on Zoom on a Friday for a social chat for the last hour.</p>
</li>
<li>
<p><strong>17:00</strong> – Finish work and help prepare dinner for the family. My husband and I share cooking duties, but my recipes tend to be more adventurous (i.e. take longer!).</p>
</li>
<li>
<p><strong>18:30—19:30</strong> – Help clean up after dinner, play with my son, and help him with his homework or reading practice.</p>
</li>
<li>
<p><strong>19:30—20:30</strong> – Bath time for my son, followed by lots of bedtime stories.</p>
</li>
<li>
<p><strong>20:30—21:30</strong> – As long as I don’t have any pressing deadlines for talks or articles, my husband and I usually watch something together. Right now we’re watching <em>The Expanse</em>, a sci-fi thriller series.</p>
</li>
<li>
<p><strong>21:30—23:00</strong> – This is usually my time for coding side projects or writing, although lately I’m struggling to do either. If you’re reading this I probably stayed up way too late to finish it. I get insomnia if I code too late, so I try to stop around <time>22:30</time> to give myself time to decompress. But if I’m deep into something then the time can run away with me.</p>
</li>
<li>
<p><strong>23:00—23:30</strong> – Bedtime. No matter how tired I am, I always read for a little while before I switch out the light. For me, that’s the most relaxing time of the day.</p>
</li>
</ul>
<p>My day is fairly regimented and there’s not a lot of down time, but having such a (necessarily) strict schedule has served me pretty well over the past couple of years. The short time I have to myself is precious and, for the most, I spend it wisely. I’ve also become pretty good at saying “no” to things I don’t have the bandwidth for. I don’t know if this post contains any useful tips for others, but I hope it gives you a little window into my world the way others have theirs. And perhaps if you’re struggling balancing your time during this pandemic, you’ll know that you aren’t alone.</p>
<h2>Read the other posts in this thread</h2>
<ul>
<li><a href="http://cdevroe.com/2021/01/07/my-typical-day/">Colin Devroe</a></li>
<li><a href="https://matthiasott.com/notes/my-typical-day">Matthias Ott</a></li>
<li><a href="https://www.sarasoueidan.com/desk/typical-day/">Sara Soueidan</a></li>
<li><a href="https://danmall.me/articles/my-typical-day/">Dan Mall</a></li>
<li><a href="https://chriscoyier.net/2021/01/08/my-typical-day/">Chris Coyier</a></li>
<li><a href="https://adactio.com/journal/17750">Jeremy Keith</a></li>
<li><a href="https://www.cassie.codes/posts/my-typical-day/">Cassie Evans</a></li>
<li><a href="https://daverupert.com/2021/01/my-typical-day/">Dave Rupert</a></li>
<li><a href="https://v6.robweychert.com/blog/2021/01/my-typical-day/">Rob Weychart</a></li>
<li><a href="https://www.antonsten.com/typical-day/">Anton Sten</a></li>
</ul>
<p>I’m tagging <a href="https://twitter.com/piccalilli_">Andy Bell</a>, <a href="https://twitter.com/hj_chen">HJ Chen</a> and <a href="https://twitter.com/hdv">Hidde de Vries</a> for the next posts.</p>
On a break2021-01-26T00:00:00Zhttps://css-irl.info/on-a-break/<p>Since starting this blog almost three years ago, I’ve regularly published two more more articles every month. I love writing and I love web development, so this never (or hardly ever) felt like a chore: there was always something to get excited about, which meant I couldn’t wait to dash off a few thoughts, whether on the train home from work or late into the evening, even after a long day of coding. But this pandemic has utterly defeated me.</p>
<p>Straight after Christmas, the UK entered its third lockdown. This one has no end in sight. Schools have closed. My husband and I are having to juggle work with homeschooling our energetic and easily-distracted 4-year-old, who is missing out on some important chances to build relationships, and doesn’t fully understand why he can’t be with his friends. We’re currently in week four, and likely have many more weeks to go.</p>
<p>In many ways I am very fortunate: I still have my job, my husband is able to take on most of the homeschooling, we have plenty of outdoor space nearby, no one in our family is ill, and our child doesn’t have special educational needs. But still, it’s exhausting. I’m finding hard to get excited about CSS, much less writing about it. There just isn’t the space.</p>
<p>So I’m taking a break from writing for a little while, and taking the pressure off myself to publish new content. I’ll likely still be making demos for fun, and might post some of them here, or dash off a quick blog post when the mood takes me. But don’t be surprised if there are no new tutorials for a while. As my very wise friend <a href="https://twitter.com/cassiecodes">Cassie</a> said to me, many people have a reduced appetite for consuming content right now. There’s more than enough out there. It’s okay not to be “always on”. You can give yourself a break.</p>
<p>I hope you’ll stick around for when I come out of this tunnel, whenever that may be. In the meantime, take care of yourselves and your loved ones 💜</p>
Goodbye, 20202020-12-30T00:00:00Zhttps://css-irl.info/goodbye-2020/<p>We all know 2020 has been a bad year. Nevertheless, I feel compelled to continue the tradition of writing my end-of-year review, to reflect on my goals and achievements (personal and professional). Writing things down can help make sense of a year that has often made very little sense. For me personally, and no doubt for many of you too, there have been many times throughout the past 12 months where I’ve felt stuck in limbo, and that I’m not achieving very much at all. So I’m writing this to celebrate the highlights and the small wins (as well as reminding myself they exist). But (as <a href="https://twitter.com/hdv">Hidde</a> also acknowledges in <a href="https://hiddedevries.nl/en/blog/2020-12-17-2020-in-review">his end-of-year post</a>), it doesn’t mean the year has been without its lowlights. Don’t feel bad if your biggest win this year has been just getting through it. It’s the same for me, and countless others.</p>
<h2>Redesigning and re-platforming this site</h2>
<p>My goal at the beginning of the year was to re-vamp this blog, and in early January I gave it a new visual treatment. I’d intended to do a major refactor and re-platform it to Eleventy at the same time, or at least earlier in the year, but in the end I didn’t end up doing so until November 2020. <a href="https://css-irl.info/launching-the-new-and-improved-css-irl/">I wrote a little</a> about the new tech stack and some of the decisions.</p>
<h2>Writing</h2>
<p>I wrote a total of 30 posts for this blog, plus one post for <a href="https://css-tricks.com/color-theming-with-css-custom-properties-and-tailwind/">CSS Tricks</a> on theming with Tailwind and custom properties. My most popular post (in terms of page views) was <em><a href="https://css-irl.info/why-i-dont-have-time-for-your-coding-challenge/">Why I Don’t Have Time For Your Coding Challenge</a></em>, a rant about the many reasons why coding challenges are not a good way to hire people. It seemed to hit a nerve for a lot of people!</p>
<p>Next year I’d like to fit in a couple more guest posts for other sites if I can. Writing for CSS Tricks in particular is always a pleasure.</p>
<h2>Speaking</h2>
<p>I was due to speak at <a href="https://cssday.nl/2021">CSS Day</a> back in June, which would’ve been the most exciting speaking opportunity of my career so far. I was also looking forward to meeting some of my web development heroes and peers in person. Needless to say, that event didn’t happen, and sadly it won’t happen in 2021 either. I hope CSS Day returns in the future, and that I get another opportunity to speak there, but in the meantime I’m grateful to have been asked.</p>
<p>I had intended to cut back a little on speaking at events and conferences compared to the previous year, and the fact that most events moved online in 2020 made me feel less inclined to chase speaking opportunities. For me, speaking is extremely hard work, made worthwhile by the friends and connections I’ve made at events. That element is almost entirely missing from online events, and for the most part I find the preparation work exhausting, without the reward of the in-person atmosphere and instant feedback.</p>
<p>That said, I was honoured to be invited to speak at a few online events this year, and the ones I agreed to were 100% worthwhile. Speaking at two events run by <a href="https://www.smashingmagazine.com/">Smashing</a> was a personal career highlight for sure. The team have put a lot of effort into making a great online conference experience, with lots of interaction. I hope that when life finally returns to something like normality, I’ll have the chance to speak in person.</p>
<h2>Learning</h2>
<p>In late 2019 I started a new job as Lead Front End Developer at <a href="https://www.atomicsmash.co.uk/">Atomic Smash</a>, so 2020 has been a great year of learning at work. Aside from working on developing our internal front end framework and processes, and completing some challenging projects, like many workplaces we’ve had to learn to work as a fully remote team. I’m hugely proud of how we’ve adapted, and the level of communication we’ve retained.</p>
<p>As for my personal web development tinkering and side projects, the things I’ve loved playing around with in CSS this year are custom properties, <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Motion_Path">Motion Path</a>, gradients and 3D transforms. I’m also really enjoying using <a href="https://greensock.com/gsap/">GSAP</a> for animation, especially the <a href="https://greensock.com/scrolltrigger/">ScrollTrigger</a> plugin. This culminated in spending a lot of time making a 3D scrolling animation based on the Radiohead song <em>Weird Fishes</em>: probably the most time-consuming demo I’ve ever made!</p>
<p class="codepen" data-height="418" data-theme-id="dark" data-default-tab="result" data-user="michellebarker" data-slug-hash="dyMQYYz" style="height: 418px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Weird Fishes">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/dyMQYYz">
Weird Fishes</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<p>Amazingly <a href="https://twitter.com/trostcodes">Alex Trost</a> featured it in his awesome newsletter, <a href="https://frontend.horse/articles/swimming-on-scroll-with-gsap/">Frontend Horse</a>.</p>
<h2>Mentoring</h2>
<p>I’ve done a bit of mentoring this year, both in work and outside of it. While I’ve had some great conversations and shared some (hopefully helpful!) resources, I feel like this is something I need to get better at. What makes it hard is not knowing how “successful” the advice is, which to an extent you just have to accept is inevitable. There are many factors that can influence someone’s progress, and mentoring is just one of them. But listening is a skill that we can all improve on.</p>
<p>At times it has also been quite tiring organising evening mentoring sessions online after a full day of work, so I need to work on striking the right balance.</p>
<h2>Personal life</h2>
<p>The one upside (if you can call it that) of this whole pandemic situation is that working from home has given me more incentive to prioritise activities away from the screen. My son started school this year, and working from home means I can walk him to school before starting work.</p>
<p>Many years ago I played the drums in a band. I haven’t played for a really long time, but this year I bought myself an electronic kit and have played more or less every day. It’s been a lifeline, and I’m so glad I did it. I’ve also been baking a lot more this year. It’s something I enjoy doing, but didn’t feel I had a lot of time for before. Since my son was born I feel like I haven’t had time for any hobbies outside of web development and writing about web development which, as I also do it for my day job, might not be the recipe for the most healthy lifestyle. So this is the year I’ve finally found a sort of equilibrium.</p>
<h2>Goals for 2021</h2>
<p>I’m not setting myself any major goals for the coming year. I have some ideas of things that I want to learn, but I’m also happy to see where things take me, and won’t beat myself up too much if my work leads me somewhere different. So mostly my goals are to take things easy, keep writing and keep making creative things. Happy New Year!</p>
Video: Using Tailwind with Wordpress2020-12-20T00:00:00Zhttps://css-irl.info/video-using-tailwind-with-wordpress/<p>Last week I had the pleasure of chatting with <a href="https://twitter.com/keithdevon">Keith Devon</a> and <a href="https://twitter.com/wpmark">Mark Wilkinson</a> of <a href="https://highrise.digital/">Highrise Digital</a>, alongside <a href="https://twitter.com/frontendben">Ben Furfie</a>, about my experience of using Tailwind CSS for building Wordpress sites. We’ve been using Tailwind at Atomic Smash for the past year, and I’ve been using it even longer. It’s been an interesting journey marrying it up with the Gutenberg block editor in Wordpress, but it’s fair to say it’s become an important part of our workflow.</p>
<p>Watch the video here:</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/7WbJBu-rBOI" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
A Utility Class for Covering Elements2020-12-06T00:00:00Zhttps://css-irl.info/a-utility-class-for-covering-elements/<figure>
<img src="https://css-irl.info/a-utility-class-for-covering-elements-01.jpg" alt="A diagonally striped square overlaid on a purple one" />
</figure>
<p>Here’s something I find myself needing to do again and again in CSS: completely covering one element with another. It’s the same CSS every time: the first element (the one that needs to be covered) has <code>position: relative</code> applied to it. The second has <code>position: absolute</code> and is positioned so that all four sides align to the edges of the first element. The full CSS looks like this:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.original-element</span> <span class="token punctuation">{</span>
<span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.covering-element</span> <span class="token punctuation">{</span>
<span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span>
<span class="token property">top</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token property">right</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token property">bottom</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token property">left</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Often it’s a heading or caption that needs to cover an image, sometimes with a translucent background colour or gradient. (I’d refer to this as an overlay.) Usually it’s a direct child of the first element which is being overlaid, but not always. Sometimes I want to overlay a pseudo element, maybe even transition or animate it for a hover state. Either way, I do it so often that it makes sense to create a utility class that covers it, rather than writing out the CSS properties longhand every time.</p>
<h2>Utility classes</h2>
<p>Utility classes are single-purpose classes that can be applied to any element in our web application code. They usually (but not always) consist of a single CSS property and value. There are whole frameworks, like the increasingly-popular <a href="https://tailwindcss.com/">Tailwind CSS</a>, that encourage a utility-first CSS methodology, where virtually all your CSS consists of applying utility classes. Even for those who have no wish to adopt such an extreme approach, many web projects include a sprinkling of utility classes to help things along.</p>
<p>As a utility class for our overlaid element would include several properties, it’s more verbose than most. Nevertheless, it does the job.</p>
<p>We can create a utility we’ll call <code>.overlay</code> to cover an element:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.overlay</span> <span class="token punctuation">{</span>
<span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span>
<span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">''</span><span class="token punctuation">;</span>
<span class="token property">top</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token property">right</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token property">bottom</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token property">left</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>While very flexible as a utility, using <code>position: absolute</code> sets the element’s position in relation to the nearest relative-position ancestor, so we need to remember to set <code>position: relative</code> on the element we want to cover (and ensure it’s an ancestor of the covering element).</p>
<p>In most cases the element I want to use as an overlay is a direct child, or a pseudo-element, so it might make sense to create further utilities to apply in those cases, which mean we don’t additionally need to set <code>position: relative</code>. Any of these three classes could instead be applied to the parent – that is, the original element that we want to cover.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.overlay-child,
.overlay-before,
.overlay-after</span> <span class="token punctuation">{</span>
<span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.overlay-child > *,
.overlay-before::before,
.overlay-after::after</span> <span class="token punctuation">{</span>
<span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span>
<span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">''</span><span class="token punctuation">;</span>
<span class="token property">top</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token property">right</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token property">bottom</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token property">left</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>For the pseudo-elements we need the extra property <code>content</code> (in this case with a value of an empty string).</p>
<p>We now have three utilities – <code>overlay-child</code> (to position a direct child element), <code>overlay-before</code> (to position a <code>::before</code> pseudo-element) and <code>overlay-after</code> (to position a <code>::after</code> pseudo-element), in addition to <code>overlay</code>, which would apply when we want to target the element doing the covering. An example of when you might use <code>overlay</code> instead of one of the parent-targeting classes is if the covering element is not a direct child but a descendent further down the DOM tree.</p>
<p class="codepen" data-height="460" data-theme-id="dark" data-default-tab="css,result" data-user="michellebarker" data-slug-hash="KKgzJQy" style="height: 460px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Overlay utility classes">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/KKgzJQy">
Overlay utility classes</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
<h2>Tip: Getting the nearest positioned parent with JavaScript</h2>
<p>While we’re on the subject, when we’re dealing with complex components and absolute positioning, a rogue <code>position: relative</code> can sometimes be a cause of layout bugs. To debug, we’ll generally want to find the nearest relative-positioned ancestor to the absolute-positioned element that’s causing us issues. Luckily, we can do that easily with a tiny bit of JavaScript!</p>
<p>In Firefox and Chrome’s dev tools, typing <code>$0</code> in the console panel will return the currently selected element. If we type <code>$0.offsetParent</code>, the nearest positioned ancestor of the currently selected element will be returned. We can then check whether it has <code>position: relative</code> in the styles panel. (Or if we want to check it in JavaScript, we can use
<code>getComputedStyle($0.offsetParent).position</code>.)</p>
<h2>Logical properties</h2>
<p>Positioning an element in this way is about to become more concise with the help of the new <code>inset</code> property. Part of the <a href="https://www.w3.org/TR/css-logical-1/#inset-properties">CSS Logical Properties</a> specification, <code>inset</code> is effectively a shorthand for the position properties <code>top</code>, <code>right</code>, <code>bottom</code>, <code>left</code>. That’s not quite the whole story – we also have the new logical properties, which allow us to achieve a similar thing: <code>inset-block-start</code>, <code>inset-block-end</code>, <code>inset-inline-start</code>, and <code>inset-inline-end</code>. If we’re using the default left-to-right <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/writing-mode">writing mode</a> this will map to <code>top</code>, <code>bottom</code>, <code>left</code>, <code>right</code> in that order, but other writing modes (as well as the <code>direction</code> property) will cause the values to be mapped differently. Right now though, we only need the <code>inset</code> property for our covered element.</p>
<p>If you’re not too familiar with writing modes or logical properties, this can feel a little confusing at first. <a href="https://www.smashingmagazine.com/2018/03/understanding-logical-properties-values/">Here’s a great explainer</a> by Rachel Andrew from a couple of years back.</p>
<p>Back to our utility classes, instead of:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.overlay</span> <span class="token punctuation">{</span>
<span class="token property">top</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token property">bottom</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token property">right</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token property">left</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>We can condense those four lines down to one:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.overlay</span> <span class="token punctuation">{</span>
<span class="token property">inset</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Replacing them in our utility classes makes our code considerably shorter:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.overlay-child,
.overlay-before,
.overlay-after</span> <span class="token punctuation">{</span>
<span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.overlay-child > *,
.overlay-before::before,
.overlay-after::after</span> <span class="token punctuation">{</span>
<span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span>
<span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">''</span><span class="token punctuation">;</span>
<span class="token property">inset</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h3>Browser support</h3>
<p>At the time of writing <code>inset</code> is only supported in Chrome and Firefox, so if you want to use it in production you’ll need to provide a fallback for non-supporting browsers. But it looks like browsers are getting behind logical properties on the whole, with many of them implementing <a href="https://caniuse.com/?search=logical%20properties">at least part of the Logical Properties specification</a> – so I’d like to think we can expect to be able to use it pretty soon!</p>
<h2>An alternative with Grid</h2>
<p>As <a href="https://twitter.com/simevidas">Šime Vidas</a> pointed out, something I neglected to mention is you might not need absolute positioning at all: we can also use CSS Grid to cover one element with another! Browser support isn’t as good, but if you don’t need to support older browsers then it’s the most concise solution, at only two lines. The code looks like this:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.element</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.overlay</span> <span class="token punctuation">{</span>
<span class="token property">grid-area</span><span class="token punctuation">:</span> 1 / 1 / -1 / -1<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>If our overlay is the only child element we don’t even need to set the grid area, but it might be a good idea to cover our backs in case we end up adding other child elements – otherwise we could end up with some undesireable side effects when our items are auto-placed on the grid.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.overlay-child,
.overlay-before,
.overlay-after</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.overlay,
.overlay-child > *,
.overlay-before::before,
.overlay-after::after</span> <span class="token punctuation">{</span>
<span class="token property">content</span><span class="token punctuation">:</span> <span class="token string">''</span><span class="token punctuation">;</span>
<span class="token property">grid-area</span><span class="token punctuation">:</span> 1 / 1 / -1 / -1<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Here’s a demo with the adapted utility:</p>
<p class="codepen" data-height="456" data-theme-id="dark" data-default-tab="css,result" data-user="michellebarker" data-slug-hash="NWRrgxX" style="height: 456px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Overlay utility classes with Grid">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/NWRrgxX">
Overlay utility classes with Grid</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
Troubleshooting Caching2020-11-30T00:00:00Zhttps://css-irl.info/troubleshooting-caching/<p>While launching the <a href="https://css-irl.info/launching-the-new-and-improved-css-irl/">new version of this site</a> recently, I came across a few issues with some browsers unexpectedly caching the old version – despite this being a total rebuild. It meant some users were still seeing the previous version of the site unless they manually cleared their cache. Clearly this is not a reasonable request to make of every user!</p>
<p>There are different reasons why files might be cached, and for the most part, caching is a good thing. Once downloaded, assets can be stored in a cache to avoid the browser repeatedly making requests and re-downloading the files. If you want to learn the ins and outs of caching, <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching">MDN docs</a> are a great place to start. This article specifically tackles caching of the unwanted variety, as described above. I’ll share some steps I took to ensure browsers serve the latest files after deploying changes.</p>
<h2>Deploying unique files</h2>
<p>If we deploy a file with a unique name, the browser will recognise it as a new file and serve it up. A commonly-used cache-busting technique is to append a unique identifier (or hash) to the names of our asset files (such as CSS and JS files). <code>style.css</code> becomes <code>style27850398694772.css</code>, for example.</p>
<p>The previous version of this site was built with <a href="https://www.gatsbyjs.com/">Gatsby</a>, which handled this out of the box. With <a href="https://www.11ty.dev/">Eleventy</a>, the static site generator I’m currently using, we need to do some work. Eleventy actually has a plugin for this, however, I’m using <a href="https://parceljs.org/">Parcel</a> to build the asset files, so it’s not going to work for us here. In order to implement the file hashing, we can do the following:</p>
<ol>
<li>Build the asset files.</li>
<li>Create a unique hash variable and write this to a data file. Ensure this is pulled into our Eleventy files as a variable.</li>
<li>Rename the asset files, appending the hash variable.</li>
</ol>
<p>Let’s break that down:</p>
<h3>Build the asset files</h3>
<p>I’m running Parcel’s <code>build</code> command to compile our CSS from Sass and bundle our JS modules, defined in the <em>package.json</em>:</p>
<pre class="language-json"><code class="language-json"><span class="token property">"prod:parcel"</span><span class="token operator">:</span> <span class="token string">"build:*"</span><span class="token punctuation">,</span>
<span class="token property">"build:css"</span><span class="token operator">:</span> <span class="token string">"parcel build src/css/styles.scss"</span><span class="token punctuation">,</span>
<span class="token property">"build:js"</span><span class="token operator">:</span> <span class="token string">"parcel build src/js/index.js"</span><span class="token punctuation">,</span></code></pre>
<p>By default these will build to a <code>dist</code> directory, but you can change the output file if you need to with <a href="https://parceljs.org/cli">Parcel’s build options</a>.</p>
<p>Running Eleventy builds our template files to the same directory. I have a script defined in my <em>package.json</em> for that too:</p>
<pre class="language-json"><code class="language-json"><span class="token property">"prod:eleventy"</span><span class="token operator">:</span> <span class="token string">"npx @11ty/eleventy"</span><span class="token punctuation">,</span></code></pre>
<p>Without hashing the files, I can easily reference them by their path in my HTML:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/styles.css<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></code></pre>
<h3>Create a unique hash</h3>
<p>In order to create a file with a unique hash, we need to write some JS. I’ve created a file, <em>onBuild.js</em>, which I can run with the following script:</p>
<pre class="language-json"><code class="language-json"><span class="token property">"hash"</span><span class="token operator">:</span> <span class="token string">"node onBuild.js"</span></code></pre>
<p>In that file, we can use a timestamp to produce a unique hash – since it will be different every time:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">let</span> hash <span class="token operator">=</span> Date<span class="token punctuation">.</span><span class="token function">now</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
hash <span class="token operator">=</span> hash<span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span></code></pre>
<p><code>Date.now()</code> outputs a number, but we want to write this to a JSON file, so I’m converting it to a string.</p>
<p>Then we can use the Node File System module to write that hash to a JSON file in our Eleventy <em>_data</em> directory:</p>
<pre class="language-js"><code class="language-js">fs<span class="token punctuation">.</span><span class="token function">writeFile</span><span class="token punctuation">(</span><span class="token string">'src/_data/version.json'</span><span class="token punctuation">,</span> hash<span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">err</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token keyword">return</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>hash<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> > src/_data/version.json</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<p>Now we are free to use this variable in our Eleventy template files. I’m using <a href="https://mozilla.github.io/nunjucks/">Nunjucks</a> as my templating language, so we can include it like this:</p>
<pre class="language-handlebars"><code class="language-handlebars"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>styles{ { version } }.css<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></code></pre>
<p>(We can apply it to our JS file in a similar way.)</p>
<h3>Rename the files</h3>
<p>In the same <em>onBuild.js</em> file, we’ll rename our compiled CSS and JS files, appending the hash:</p>
<pre class="language-js"><code class="language-js">fs<span class="token punctuation">.</span><span class="token function">rename</span><span class="token punctuation">(</span><span class="token string">'dist/styles.css'</span><span class="token punctuation">,</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">dist/styles</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>hash<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">.css</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">err</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token keyword">return</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">dist/styles.css > dist/styles</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>hash<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">.css</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
fs<span class="token punctuation">.</span><span class="token function">rename</span><span class="token punctuation">(</span><span class="token string">'dist/index.js'</span><span class="token punctuation">,</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">dist/index</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>hash<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">.js</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">err</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token keyword">return</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token string">dist/index.js > dist/index</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>hash<span class="token interpolation-punctuation punctuation">}</span></span><span class="token string">.js</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<p>We need to run this script on build, but it’s dependent on the CSS and JS files already existing in the <em>dist</em> directory (our build directory). I use the package <code>npm-run-all</code> to specify when to run scripts concurrently or sequentially. Back in our <em>package.json</em>, using the <code>run-s</code> command we can ensure the <code>hash</code> script runs <em>after</em> the command that builds the CSS and JS files:</p>
<pre class="language-json"><code class="language-json"><span class="token property">"build"</span><span class="token operator">:</span> <span class="token string">"run-s prod:parcel hash"</span><span class="token punctuation">,</span></code></pre>
<p>Lastly once the CSS and JS files are built and the hash variable created and appended to the file names, we can compile our Eleventy template files, safe in the knowledge that the hash variable will exist. So let’s append our Eleventy build command to the <code>build</code> script, so that the commands are run in sequence:</p>
<pre class="language-json"><code class="language-json"><span class="token property">"build"</span><span class="token operator">:</span> <span class="token string">"run-s prod:parcel hash prod:eleventy"</span><span class="token punctuation">,</span></code></pre>
<h3>Removing the hash in dev mode</h3>
<p>When I’m running Parcel in <code>watch</code> mode (i.e. while I’m developing my site), I don’t want to pull in the <code>hash</code> variable, as Parcel will only create the un-hashed files at that point. So additionally we can create a script file (which I’m calling <em>onStart.js</em>) to run when I run the <code>npm start</code> command:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> fs <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'fs'</span><span class="token punctuation">)</span>
fs<span class="token punctuation">.</span><span class="token function">writeFile</span><span class="token punctuation">(</span><span class="token string">'src/_data/version.json'</span><span class="token punctuation">,</span> <span class="token string">''</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">err</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token keyword">return</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span><span class="token string">''</span><span class="token interpolation-punctuation punctuation">}</span></span><span class="token string"> > src/_data/version.json</span><span class="token template-punctuation string">`</span></span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<p>This removes the hash variable in the <em>version.json</em> data file, ensuring it will not be appended to the file names referenced in our Nunjucks files.</p>
<h3>Limitations</h3>
<p>I’m currently only hashing the CSS and JS files, not other asset files such as images, which means they still run the risk of being cached. Dealing with images in this way would be more complex, and as they’re unlikely to change very often I decided not to take those extra steps in this case.</p>
<h3>Content-based versioning</h3>
<p>It’s worth bearing in mind that if we’re using a timestamp, the files will be re-hashed on every build. Users will have to download new files, even if the file contents themselves haven’t changed. For content-based hashing we could instead use the <a href="https://github.com/pvorb/node-md5">MD5</a> package. The step to implement content-based hashing are a bit more extensive than we’ll cover here, but my Eleventy starter project, <a href="https://eleventy-parcel.netlify.app/">Eleventy-Parcel</a> has content-based versioning built in.</p>
<h2>Set cache-control HTTP headers</h2>
<p>Additionally, we can prevent browsers caching our HTML pages by setting <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control">HTTP headers</a>. My site is hosted with <a href="https://www.netlify.com/">Netlify</a>, which means I can specify the headers in a <em>netlify.toml</em> configuration file:</p>
<pre class="language-toml"><code class="language-toml"><span class="token punctuation">[</span><span class="token punctuation">[</span><span class="token table class-name">headers</span><span class="token punctuation">]</span><span class="token punctuation">]</span>
<span class="token key property">for</span> <span class="token punctuation">=</span> <span class="token string">"/*"</span>
<span class="token punctuation">[</span><span class="token table class-name">headers.values</span><span class="token punctuation">]</span>
<span class="token key property">cache-control</span> <span class="token punctuation">=</span> <span class="token string">'''
max-age=0,
no-cache,
no-store,
must-revalidate'''</span></code></pre>
<p>By setting <code>max-age="0"</code>, we ensure that the browser considers the file stale, prompting it to revalidate with the server.</p>
<p>We can also set cache-control headers as HTML meta tags:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span>
<span class="token attr-name">http-equiv</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Cache-Control<span class="token punctuation">"</span></span>
<span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>no-store, no-store, must-revalidate<span class="token punctuation">"</span></span>
<span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">http-equiv</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Pragma<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>no-cache<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">http-equiv</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Expires<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></code></pre>
<p>But this is considered inferior because the meta tags are only honoured by a <em>some</em> browser caches, not proxy caches. I had to do a bit of digging to find out about the difference between using meta tags versus HTTP headers, but <a href="https://www.mnot.net/cache_docs/#META">this article</a> explains it well.</p>
<h2>Service workers</h2>
<p>Even after taking all of the above steps, I still had some problems with my site caching. It turned out, the answer was a service worker. Service workers are Javascript workers that run off the main thread, separate from your web application. They can handle all sorts of tasks, notably intercepting network requests and caching responses. Service workers are a pretty big topic themselves, so I’m not going to delve into the details here. But if you’re interested in further reading, I recommend checking out <a href="https://developers.google.com/web/fundamentals/primers/service-workers">Google’s official documentation</a>.</p>
<p>To see any service workers registered for your site in Chrome, open the developer tools panel and go to the ‘Application’ tab. In the ‘Application’ menu, you should see ‘Service workers’.</p>
<p>The interesting thing about my new site was that it <em>didn’t</em> have a service worker – yet in my dev tools I was still seeing a service worker registered. Why? Because the old version of the site had a service worker, and it hadn’t been unregistered. You can unregister a service worker from the dev tools panel, but of course we can’t expect every user to do that! So we need to unregister the old service worker.</p>
<h3>Unregistering service workers</h3>
<p>Service workers are registered in Javascript – and they can also be unregistered. To check for existing service workers and unregister any that exist, we can add the following to our main JS file:</p>
<pre class="language-js"><code class="language-js">navigator<span class="token punctuation">.</span>serviceWorker<span class="token punctuation">.</span><span class="token function">getRegistrations</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">registrations</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> registration <span class="token keyword">of</span> registrations<span class="token punctuation">)</span> <span class="token punctuation">{</span>
registration<span class="token punctuation">.</span><span class="token function">unregister</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">unregistered</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>
unregistered <span class="token operator">==</span> <span class="token boolean">true</span> <span class="token operator">?</span> <span class="token string">'unregistered'</span> <span class="token operator">:</span> <span class="token string">'failed to unregister'</span>
<span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<h3>Updating the service worker</h3>
<p>Another option is to install a new service worker to update the old one. Google’s documentation has this to say about updating service workers:</p>
<blockquote>
<p>When the user navigates to your site, the browser tries to redownload the script file that defined the service worker in the background. If there is even a byte's difference in the service worker file compared to what it currently has, it considers it new.</p>
</blockquote>
<p>So updating the service worker (or registering it anew) should effectively overwrite the old one. In my project, I created the file <em>service-worker.js</em>, and ensured this was built with Parcel by updating my <code>build.js</code> command:</p>
<pre class="language-json"><code class="language-json"><span class="token property">"build:js"</span><span class="token operator">:</span> <span class="token string">"parcel build src/js/index.js src/js/service-worker.js"</span></code></pre>
<p>First we need to register the new service worker in our main JS file. We first check that the browser supports service workers, then register the new service worker on load:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token string">'serviceWorker'</span> <span class="token keyword">in</span> navigator<span class="token punctuation">)</span> <span class="token punctuation">{</span>
window<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'load'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
navigator<span class="token punctuation">.</span>serviceWorker<span class="token punctuation">.</span><span class="token function">register</span><span class="token punctuation">(</span><span class="token string">'/service-worker.js'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span>
<span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">registration</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>
<span class="token string">'ServiceWorker registration successful with scope: '</span><span class="token punctuation">,</span>
registration<span class="token punctuation">.</span>scope
<span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">err</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'ServiceWorker registration failed: '</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span></code></pre>
<p>When a user visits your site, the service worker will be installed. It then enters a “waiting” state – if there is an existing service worker, the new service worker will wait until the page is refreshed in order to take control.</p>
<p>In the service worker file itself (<em>service-worker.js</em> in this project), we can specify callbacks to run when the service worker is installed and activated. On install, we can call <code>self.skipWaiting()</code> to instruct it to take control straight away:</p>
<pre class="language-js"><code class="language-js">self<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'install'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
self<span class="token punctuation">.</span><span class="token function">skipWaiting</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<p>Now, if we want to unregister any old service workers (including this one!), we can run a callback on the <code>activate</code> event:</p>
<pre class="language-js"><code class="language-js">self<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'activate'</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
self<span class="token punctuation">.</span>registration<span class="token punctuation">.</span><span class="token function">unregister</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'unregister'</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">catch</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">err</span><span class="token punctuation">)</span> <span class="token operator">=></span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<p>We probably want to do a bit more with our service worker than just remove an existing service worker, but that’s beyond the scope of this article.</p>
<h3>Clearing the cache with a service worker</h3>
<p>This did the job of unregistering the problematic service worker, and initially it appeared to be enough. But it turned out that some users were still seeing the old version of the site until they performed a hard reload. The culprit turned out to be <a href="https://www.gatsbyjs.com/plugins/gatsby-plugin-offline/">gatsby-plugin-offline</a>, a plugin I had installed on my old Gatsby site. Even though I wasn’t using Gatsby anymore, when I opened the cache in the browser dev tools, I could still see several files related to that plugin in there.</p>
<p>In the end, I was able to resolve this by using my new service worker to clear the cache:</p>
<pre class="language-js"><code class="language-js">self<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'install'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
self<span class="token punctuation">.</span><span class="token function">skipWaiting</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
self<span class="token punctuation">.</span>caches
<span class="token punctuation">.</span><span class="token function">keys</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">keys</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
keys<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">key</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>key<span class="token punctuation">)</span>
self<span class="token punctuation">.</span>caches<span class="token punctuation">.</span><span class="token function">delete</span><span class="token punctuation">(</span>key<span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
self<span class="token punctuation">.</span>registration<span class="token punctuation">.</span><span class="token function">unregister</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'unregister'</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">catch</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">err</span><span class="token punctuation">)</span> <span class="token operator">=></span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token punctuation">)</span></code></pre>
<p>Lastly, we can take the optional step of retrieving a list of any open tabs under the service worker’s control and forcing them to reload, rather than the user having to do it manually. <code>self.clients.matchAll()</code> retrieves a list of open browser tabs. Our service worker file now looks like this:</p>
<pre class="language-js"><code class="language-js">self<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'install'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">e</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
self<span class="token punctuation">.</span><span class="token function">skipWaiting</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
self<span class="token punctuation">.</span>caches
<span class="token punctuation">.</span><span class="token function">keys</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">keys</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
keys<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">key</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>key<span class="token punctuation">)</span>
self<span class="token punctuation">.</span>caches<span class="token punctuation">.</span><span class="token function">delete</span><span class="token punctuation">(</span>key<span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
self<span class="token punctuation">.</span>registration<span class="token punctuation">.</span><span class="token function">unregister</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">'unregister'</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
self<span class="token punctuation">.</span>clients<span class="token punctuation">.</span><span class="token function">matchAll</span><span class="token punctuation">(</span><span class="token punctuation">)</span>
console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>self<span class="token punctuation">.</span>clients<span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">clients</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
clients<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">client</span><span class="token punctuation">)</span> <span class="token operator">=></span> client<span class="token punctuation">.</span><span class="token function">navigate</span><span class="token punctuation">(</span>client<span class="token punctuation">.</span>url<span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">.</span><span class="token function">catch</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">err</span><span class="token punctuation">)</span> <span class="token operator">=></span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span><span class="token punctuation">)</span></code></pre>
<h2>Resources</h2>
<p>I learnt a lot about caching and service workers during this process! Here are some of the resources that helped me:</p>
<ul>
<li><a href="https://csswizardry.com/2019/03/cache-control-for-civilians/">Cache Control for Civilians</a> by Harry Roberts</li>
<li><a href="https://www.mnot.net/cache_docs/#META">HTML Meta Tags and HTTP Headers</a></li>
<li><a href="https://love2dev.com/blog/how-to-uninstall-a-service-worker/">How to uninstall a service worker</a></li>
<li><a href="https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle">The Service Worker Lifecycle</a> by Jake Archibald</li>
</ul>
Color Theming with CSS Custom Properties and Tailwind2020-11-20T00:00:00Zhttps://css-irl.info/color-theming-with-custom-properties-and-tailwind/<p>I’m a big fan of custom properties, and this post for CSS Tricks covers how we use them with Tailwind CSS at Atomic Smash for building themes.</p>
Launching the New and Improved CSS { In Real Life }2020-11-04T00:00:00Zhttps://css-irl.info/launching-the-new-and-improved-css-irl/<p>After a few months of on-and-off work, this week I’m pleased to finally launch the new and improved version of this site! It’s not a major redesign, and if you’re just reading articles you might notice very little difference. But there are a few new features I hope users might enjoy. No doubt there are a few bugs to fix too! Please be patient while I iron those out over the coming weeks. 😉</p>
<h2>New features</h2>
<h3>Re-vamped homepage</h3>
<p>The old homepage always felt a little bland. The new version gives more prominence to the latest article, and features a selection of popular topic tags, to make searching for an article a little easier. You can still click through to the “tags” page (via the <a href="https://css-irl.info/tags">all topics</a> link) for a full list of the topics covered.</p>
<h3>Blogroll</h3>
<p>Adding a <a href="https://css-irl.info/blogroll">blogroll</a> to a site is becoming increasingly popular among the web dev community, as personal blogs are experiencing a resurgence. It’s a great way to find content without overly relying on Twitter or other social networks. <a href="https://css-irl.info/blogroll">This is a list of some of the people I’m inspired by</a>, whose blogs I read regularly. (I’m sure I’ve probably missed a few!) I’ll keep adding to this section over time.</p>
<h3>About page</h3>
<p>The <a href="https://css-irl.info/about">About</a> page was always fairly minimal, mainly because I hate writing about myself! But, almost three years into this blog, I decided I wanted to tell the story of how it came to be, and why I continue to write about web development. It’s not a page I expect to be frequently visited, but it’s always there, and easy to find.</p>
<h3>Fly out menu</h3>
<p>With the number of page growing, the addition of a fly-out menu means I don’t need to worry about fitting an increasing number of links into the header. This is a way of keeping them neatly on-hand, and it’s keyboard-accessible of course.</p>
<h2>Behind the scenes</h2>
<h3>Eleventy</h3>
<p>The main driving force behind the “new” release is a move from static site generator <a href="https://www.gatsbyjs.com/">Gatsby</a> to <a href="https://www.11ty.dev/">Eleventy</a>. I’ve written before about my <a href="https://css-irl.info/from-gatsby-to-eleventy/">motivations for the change</a>, and for the most part it’s been fairly painless – aside from a few minor sticking points, which are inevitable during the learning process. I have plans for the future of this site, which I feel more confident pressing ahead with now, with Eleventy as part of the toolkit.</p>
<p>Moving to Eleventy has also been an opportunity to refactor and remove dead code, as it was getting a little messy. Although I was able to copy over <em>some</em> of the CSS, I decided to move from <a href="https://github.com/css-modules/css-modules">CSS Modules</a> to a more traditional Sass architecture. I don’t hate CSS Modules, but overall I feel happier using BEM and regular Sass.</p>
<h3>Parcel</h3>
<p>I’m using <a href="https://parceljs.org/">Parcel</a> for module bundling and compiling assets. I’ve been using Parcel for the best part of a year, and I’m pretty happy with it as an alternative to Webpack. I wrote a <a href="https://css-irl.info/a-modern-front-end-workflow-part-2/">guide to getting started with Parcel</a> earlier this year, and the site itself is based on my <a href="https://eleventy-parcel.netlify.app/">Eleventy-Parcel</a> starter project. If you have a complex site and need more configuration options then Parcel might not be for you (it’s famously “zero-config”). But for my needs right now, it works just fine.</p>
<h2>Future goals</h2>
<p>There are a few new features that I didn’t get around to adding for this release, but I’d like to add in the not-too-distant future.</p>
<h3>Dark mode toggle</h3>
<p>While the site currently has dark mode support (through the <code>prefers-colour-scheme</code> media query, <a href="https://css-irl.info/quick-and-easy-dark-mode-with-css-custom-properties/">chronicled here</a>), it’s better for the user to be able to control this and switch back to light mode if they wish, without having to change their system settings. I’m aiming to add this pretty soon.</p>
<h3>Related content</h3>
<p>I’d like to display related articles at the end of blog posts, help users more easily access information on similar topics to the one they’re reading about.</p>
<h3>Recommended resources</h3>
<p>In addition to the blogroll, I’m planning to add other recommended resources, such as podcasts, books, videos or courses. This blog is all about sharing and helping people to learn about web development. Sharing resources from others is part of that.</p>
<p>What are the features you’d like to see on CSS { IRL }? I’d love to hear your feedback!</p>
Tailwind Thoughts2020-10-22T00:00:00Zhttps://css-irl.info/tailwind-thoughts/<p>Although I use utility class framework <a href="https://tailwindcss.com/">Tailwind CSS</a> for work, in some ways I am a reluctant user. I actively advocated for us to adopt it as a team, but there’s still something about it that doesn’t feel quite as good (for me) as writing “real” CSS. It has its limitations which, I believe, are worth considering before wholesale adoption. (I wrote about <a href="https://css-irl.info/a-year-of-utility-classes/">my feelings towards it</a> some time ago.) Nevertheless, its many advantages – the speed which it allows for building components, the consistency it brings to projects that might have several different team members working on the front end, the clear documentation – make it a worthwhile choice for us at <a href="https://www.atomicsmash.co.uk/">Atomic Smash</a>.</p>
<p>Since I started using Tailwind more than two years ago, plenty of new features have been added that make it easier and more appealing to use. One of my gripes was that I frequently had to jump back into “real” CSS territory if I wanted to do anything remotely complex with CSS – working with complex grids, dealing with browser support issues via <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@supports">feature queries</a>, etc – which often meant refactoring, and could be time-consuming. Happily, I’m finding that I need to do that less and less. I do still find it easier to work in a Sass partial for particularly complex styling, but this is less of an issue than before, as it’s usually quite isolated cases. These days Tailwind ships with a bunch of utility classes for CSS Grid, transforms, gradients and much more. It also bundles <a href="https://purgecss.com/">PurgeCSS</a>, so there’s no need to install this separately. Since this addition I’ve run into a lot fewer problems with incorrect styles being purged, which has made the whole process much smoother.</p>
<p>I’m also impressed by the Tailwind team’s commitment to tackling common styling issues. Little things like the <code>divide-*</code> classes, which apply <a href="https://tailwindcss.com/docs/divide-width">borders between elements</a> by targeting the children of a container. These things aren’t difficult to do with CSS, but having those utility classes to hand can end up saving time.</p>
<h2>Custom property magic</h2>
<p>I was interested to read recently, in an article by Tailwind’s creator <a href="https://twitter.com/adamwathan">Adam Wathan</a>, about <a href="https://adamwathan.me/composing-the-uncomposable-with-css-variables/">how the team uses custom properties (CSS variables)</a> to compose various utility classes. It’s well worth reading about how they came up with some pretty creative solutions, such as deliberately using whitespace as a valid property value in order to compose shorthand properties. Interestingly, <a href="https://lea.verou.me/2020/10/the-var-space-hack-to-toggle-multiple-values-with-one-custom-property/">Lea Verou</a> wrote about a similar technique <a href="https://lea.verou.me/2020/10/the-var-space-hack-to-toggle-multiple-values-with-one-custom-property/">on her blog</a>, so perhaps this is a technique that’ll gain consensus. (It’s worth noting Adam’s observation at the end of his piece about how most CSS minifiers will currently strip this out in production though.)</p>
<p>It’s refreshing to see how Tailwind has evolved, and interesting to see its creators leveraging modern CSS specifications to make it more powerful. I’m looking forward to seeing how it evolves in the future to support the increasingly mighty CSS landscape.</p>
Making Work Experience a Positive Experience2020-10-08T00:00:00Zhttps://css-irl.info/making-work-experience-a-positive-experience/<figure>
<img src="https://css-irl.info/making-work-experience-a-positive-experience-01a.svg" alt="A stylised illustration of a Zoom meetng with four people on screen" />
</figure>
<p>Last week at <a href="https://www.atomicsmash.co.uk/">Atomic Smash</a> we had <a href="http://aliceviccajee.co.uk/">Alice</a>, a junior developer, join us for some work experience. Helping someone get the most out of a work experience placement whilst maintaining the existing team’s productivity is challenging at the best of times. But during a global pandemic, with a fully remote team, it becomes even more so. As lead front end developer, it fell to me to oversee a large part of the placement, and provide assistance when needed.</p>
<p>In this article I’ll share how we approached this. I hope these tips and ideas might help others feel more comfortable with providing remote work experience placements or internships.</p>
<h2>1. Team communication</h2>
<p>A big part of making someone feel at ease on an internship or work experience placement is including them as part of the team during the time they are with us. For us, that means inviting them to team meetings, adding them to our Slack workspace, proving them with the appropriate level of access for the apps we regularly use, and making sure they can join our team social on a Friday. It also means maintaining a line of communication (mainly via Slack), and making them aware who to reach out to with questions at any time. We used Zoom frequently for training and giving feedback, which reflects how we work as a team.</p>
<p>I’m grateful that at Atomic Smash we’ve managed to work well together and deliver projects to a high standard throughout this period of transition to a fully remote workforce. That’s in no small part down to everyone’s commitment to maintaining a high level of communication through our various channels, and already having this grounding within the company meant that it was easier to extend it to work shadowing and training.</p>
<h2>2. Accepting a drop in productivity</h2>
<p>When someone new joins a team it’s inevitable that existing team members will need to take the time to onboard them. With a junior team member or someone on a placement, there is likely to be a bit more ongoing support needed. This needs to be communicated to the project manager, and planned for accordingly. It’s likely to affect some team members more than others, depending on the type of assistance and training required.</p>
<p>I found that a over couple of days my productivity was hugely impacted, when I needed to spend a lot of time on training and orientation, but other days I was able to get on with my work, while being “on call” in case I needed to help with anything. Taking on a work experience placement when you have a pressing client deadline probably isn’t a great idea, as it would inevitably make the person doing the placement feel uncomfortable bothering established team members with questions – and the freedom to ask for help when necessary is super important to helping new team members feel relaxed and do their best work.</p>
<h2>3. Orientation</h2>
<p>Alice spent the first few days shadowing different members of the development team to get some insight into the different types of work we do, from support and maintenance through to building brand new projects from the ground up, as well as our tools and internal processes. On the front end side, I spent an entire day with her just talking through our project process and the technologies we use, including demonstrating them in action by screensharing and building some front end components while talking through my development decisions. The aim wasn’t that she should remember everything perfectly, but that she should feel orientated within the project when it came to writing code herself. I fully expected that she would still need to frequently reach out and ask questions, and made sure she was aware that she could do so at any time.</p>
<p>The onboarding involved <em>a lot</em> of talking on my part. I found it a bit more draining than I would have if we’d done it in-person, as it’s a little more difficult to gauge whether someone is taking everything in, but doing this over a video call was relatively painless on the whole.</p>
<h2>4. Experimentation</h2>
<p>We use <a href="https://tailwindcss.com/">Tailwind</a> for writing our CSS at Atomic Smash, so once I’d given Alice an intro, I invited her to spend a bit of time building a small Codepen demo, to allow her to get more familiar without the stress of thinking about everything else the project entails. Personally, I feel that having the time and space to try out unfamiliar tools and technologies in a relaxed environment can help speed up the process when it comes to moving into a real project environment.</p>
<h2>5. Writing code</h2>
<p>I wanted to give Alice the chance to contribute some real code to the project, but didn’t want her to feel pressured to deliver a final, polished outcome within a short deadline, especially while she was learning the ropes. At Atomic Smash we break our tasks down into milestones, so I assigned her some tasks from one of the later milestones, that was less time-sensitive. I gave her plenty of time to work on each task, checking in with her towards the end of the day. We then did a code review (over Zoom), where I talked through any parts that I would do differently and why. I made it clear that those differences weren’t things I would expect her to know, more like things that come with the experience of being embedded with the technology and the project for an extended length of time.</p>
<h2>Conclusion</h2>
<p>Taking on a junior developer or work experience placement during a global pandemic need not be a headache – it just needs planning, consideration and empathy. I hope these tips give you some ideas about how to approach this, and the kind of support you can offer.</p>
<p>(Alice is currently looking for work – you should <a href="http://aliceviccajee.co.uk/">hire her</a>!)</p>
Quick Command Line Tip: Create Mutliple Files With the Same Extension2020-09-25T00:00:00Zhttps://css-irl.info/quick-command-line-tip-create-multiple-files/<p>If you’re familiar with the command line, you’ll probably already know you can create a new file using the <code>touch</code> command. For example, this command will create a new <em>index.html</em> file in your current directory:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">touch</span> index.html</code></pre>
<p>How about creating multiple files? Sure, we can do that all in one command:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">touch</span> index.html styles.css index.js</code></pre>
<p>When it comes to multiple files with the same extension then sure, we could use the above method. But what about if you want to create 10, 20, 100 or more files? That could get a little tedious.</p>
<p>In my current workflow, I use <a href="https://mozilla.github.io/nunjucks/">Nunjucks</a> as a templating language, and I typically work with tens or even hundreds of components in template partials. As part of our development planning process at <a href="https://www.atomicsmash.co.uk/">Atomic Smash</a>, we like to create these files upfront, before development begins in earnest. This is so that several developers can work on the project simultaneously using the same file naming conventions, and adding comments into the component files to plan the data that needs to feed into them.</p>
<p>If you need to create multiple files with the same extension, then the following line of code definitely speeds up that process. Just replace the content inside the curly brackets with your comma-separated list of file names, and the <code>.njk</code> extension with your desired file extension:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">touch</span> <span class="token punctuation">{</span>hero,lightbox,form,another-component<span class="token punctuation">}</span>.njk</code></pre>
<p>This command creates the following Nunjucks files:</p>
<pre><code>hero.njk
lightbox.njk
form.njk
another-component.njk
</code></pre>
<p>I hope this saves you a bit of time like it does for me!</p>
Learning About CSS 3D Transforms and Perspective2020-09-15T00:00:00Zhttps://css-irl.info/learning-about-css-3d-transforms/<p>3D transforms, despite being available in CSS for a while, are one area I’ve never understood well. I think if they were new to CSS now, I’d spend a lot more time playing around with them, like I have with Motion Path and some other new CSS features. As things stand, I’ve never had much cause to use them in production (with the odd exception), so I’ve concentrated my time and energy on other things.</p>
<p>But recent work by talented folks on Codepen had me wishing I had a better grasp of this powerful area of CSS. I fell in love <a href="https://codepen.io/cobra_winfrey/full/mdJWzXQ">this demo by Adam Kuhn</a>, which combines 3D transforms with Motion Path – I can see lots more potential for using these together. <a href="https://twitter.com/anatudor">Ana Tudor</a> built an <a href="https://codepen.io/thebabydino/pen/BevRMj">entire series of Johnson Solids</a>, which get progressively more complex. She has an impressive body of work coding polyhedra, which can be found in <a href="https://codepen.io/collection/eErLu">this collection</a>. Not to mention this quite simply mind-blowing demo my <a href="https://twitter.com/amit_sheen">Amit Sheen</a>, where pages of a book are actually animated with 3D transforms:</p>
<iframe height="402" style="width: 100%;" scrolling="no" title="Turning pages with CSS" src="https://codepen.io/amit_sheen/embed/WNweryv?height=402&theme-id=dark&default-tab=result" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/amit_sheen/pen/WNweryv">Turning pages with CSS</a> by Amit Sheen
(<a href="https://codepen.io/amit_sheen">@amit_sheen</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>I figured it was time to learn! I came across this <a href="https://css-tricks.com/how-css-perspective-works/">well-written article</a>, also by Amit Sheen, explaining 3D transforms and perspective. The interactive examples, coupled with a step-by-step walk-through creating an animated cube, really helped things click. I’m looking forward to building a lot more with 3D transforms in the future, and feeling much more confident about it!</p>
<p>To try out my newfound knowledge, I made a little CSS animation inspired by a GIF I saw online. It uses a single <code><div></code>, with multiple repeating background gradients, 3D transforms and animated <code>background-position</code>. (You can’t animate gradients with CSS alone, but you <em>can</em> animate <code>background-position</code>).</p>
<iframe height="476" style="width: 100%;" scrolling="no" title="Single-div gradient grid" src="https://codepen.io/michellebarker/embed/RwaygLa?height=476&theme-id=dark&default-tab=result" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/RwaygLa">Single-div gradient grid</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>I’ve also made a couple of demos showing how several elements’ transforms are altered by changing the <code>transform-style</code> and <code>perspective-origin</code> properties of the parent – something that took me a while to get my head around:</p>
<iframe height="413" style="width: 100%;" scrolling="no" title="Transform-style" src="https://codepen.io/michellebarker/embed/abNKPqO?height=413&theme-id=dark&default-tab=result" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/abNKPqO">Transform-style</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<iframe height="423" style="width: 100%;" scrolling="no" title="Perspective-origin" src="https://codepen.io/michellebarker/embed/OJNErew?height=423&theme-id=dark&default-tab=result" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/OJNErew">Perspective-origin</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>I still feel I have a lot to learn, but the only way is through practice!</p>
<h2>Inspiration</h2>
<p>Some more inspiring demos from talented folks:</p>
<ul>
<li><strong><a href="https://codepen.io/amit_sheen/pen/vYGdBNo">Climbing up the stairs</a></strong> – yet another incredible demo from Amit Sheen, where the text animates up a flight of stairs</li>
<li><strong><a href="https://codepen.io/collection/AaPGwd">CSS 3D Transforms and animations</a></strong> – this collection by <a href="https://twitter.com/petebarr">Pete Barr</a> has just too many amazing examples to pick just one...but if I have to choose, I’m rather partial to <a href="https://codepen.io/petebarr/pen/MWKgmYW">this one</a>, which responds to scroll</li>
<li><strong><a href="https://codepen.io/jh3y/pen/KKVjLrx">CSS 3D Animated Toaster</a></strong> – this cute little toaster animation is, like everything <a href="https://twitter.com/jh3yy">Jhey</a> does, an absolute delight!</li>
<li><strong><a href="https://codepen.io/collection/c7ee87c195ce7c6b5d8608803ac1bab0">3D CSS</a></strong> – some more spectacular 3D magic from <a href="https://twitter.com/cobra_winfrey">Adam Kuhn</a>, whose continuously high-quality creative output is unrivalled</li>
</ul>
Why I Don’t Have Time For Your Coding Challenge2020-09-10T00:00:00Zhttps://css-irl.info/why-i-dont-have-time-for-your-coding-challenge/<p>It tends to be standard recruitment practice in the tech industry to require candidates to complete some sort of coding test or challenge. Sometimes this takes the form of the much-feared whiteboard interview – where candidates are expected to work through a problem on the aforementioned whiteboard, in front of an interviewing panel. In other cases, it’s a take-home assignment, or coding challenge. Often following an initial interview (or sometimes two rounds of interviews), the candidate is given a task to complete in their own time, generally something typical of the job they are applying for, or that requires many of the same skills.</p>
<p>This article is not specifically about the whiteboard interview, which I have never been through myself, although many of my points are equally applicable (perhaps more so). Rather it is about the utter uselessness of the “coding challenge”, and why I would never advocate using this to assess prospective candidates for your job vacancy. Having been a candidate for front end developer positions on several occasions, I am 100% convinced that coding challenges are a waste of time and resources, and cause unnecessary stress for the prospective. I particularly enjoyed this Tweet from <a href="https://twitter.com/laurieontech">Laurie</a>:</p>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Ok, I’ll do one.<br /><br />Tech interviews can be done well without the candidate ever writing a line of code.<br /><br />And the idea that they can’t is more about the interviewer than the interviewee. <a href="https://twitter.com/hashtag/devdiscuss?src=hash&ref_src=twsrc%5Etfw">#devdiscuss</a></p>— Laurie (@laurieontech) <a href="https://twitter.com/laurieontech/status/1303503079359295490?ref_src=twsrc%5Etfw">September 9, 2020</a></blockquote> <script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>(If you don’t want to read this whole article, read her Twitter thread instead!)</p>
<p><strong>Disclaimer:</strong> I’m writing this as an interviewee, having never been on the other side of the interview table (for a web development position, at any rate). Perhaps you’re an interviewer and feel that coding challenges are important and valuable. I would argue that even if that <em>is</em> the case (which is doubtful itself), they are not worth the burden you put upon the candidate.</p>
<h2>Time-consuming</h2>
<p>The number one reason I believe coding challenges shouldn’t be conducted is the amount of time they take to complete. Looking for a job is tiring and often demoralising. The chances are your candidates are already spending many, many hours writing out applications and tailoring their resumés. Many coding challenges come with caveats like “only spend six hours on this”, but in many cases candidates will spend much more time than stated. Why wouldn’t you, when you know there’s a chance that other applicants are spending more time, delivering a more polished result than you?</p>
<p>For this reason, coding challenges are anti-inclusive, only encouraging applications from people with unlimited free time. For candidates who are parents, have other caring responsibilities, work multiple jobs, have medical conditions, are studying in their spare time (the list goes on), six hours work for a job that they might not even get is time they simply don’t have.</p>
<h3>Spec work: just don’t</h3>
<p>A special mention here for “spec work”: It should go without saying, but requiring candidates to complete tasks that would otherwise be part of a paid job role is a complete no-no. I shouldn’t have to explain why this is bad, but it’s safe to say that if you’re expecting your candidates to do anything like the amount of work this challenge involves, you should be compensating them accordingly.</p>
<h2>Counter-productive</h2>
<p>People do not do their best work when put under pressure. The whiteboard interview is a classic example of how putting someone in an anxiety-inducing environment is unlikely to yield the best results, but the coding challenge is stressful in its own way too. To be writing code knowing that every line will be scrutinised and judged to determine your future, means that you’re unable to concentrate fully on just writing code that works. You’re second-guessing all the time – “Will they approve of the way I’ve constructed this function?”, etc – which will likely lead to overthinking, sucking up the time you have available, and therefore requiring more of your time.</p>
<p>On the other hand, perhaps the challenge is easy for the candidate, and they’ll simply sail through it. Take the “to do” list application – a fairly common coding challenge example. It’s quite possible they’ll have built something like for an interview before, and this “challenge” will be re-hashing the same. In this case, you’re not learning anything that couldn’t be gained from looking at their previous work, and it wastes everybody’s time.</p>
<p>Some candidates may stick to the safety of their comfort zones, and focus on delivering something technically polished but less advanced. Others may overstretch themselves, taking on something new, only to fall short (while stressing themselves out in the process). An example from my own experience: apply for a junior developer role, I was required to build a landing page from a design. Although I had barely any experience with Javascript, it was one of the “desirable” job requirements, so I spent a disproportionate amount of time trying to add some JS functionality, meaning I had less time to do the things I was confident in doing well. I went into the interview already feeling like a failure. Either way, a coding challenge is an artificial environment, that doesn’t reflect the real world where an employee will feel less pressure. I repeat: it’s in no way an indicator of how they will behave in a working environment.</p>
<p>For these reasons, a coding challenge is unlikely to demonstrate what a candidate is truly capable of.</p>
<h2>Arbitrary</h2>
<p>A disproportionate amount of time is often spent trying to deduce the requirements of the challenge. What is actually being judged? In the previous example, I didn’t know whether I would be judged poorly for my mediocre JS implementation, or looked upon favourably for having given it a go. Another case in point is this tweet from <a href="https://twitter.com/carolstran">Carolyn Stransky</a>:</p>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">When you see this in a frontend coding challenge... do you think they actually don’t care or do they really care? (poll below)<br /><br />The wink is throwing me off 🙎🏼♀️ <a href="https://t.co/OJUKeNgyWb">pic.twitter.com/OJUKeNgyWb</a></p>— Carolyn Stransky (@carolstran) <a href="https://twitter.com/carolstran/status/1297215015171371009?ref_src=twsrc%5Etfw">August 22, 2020</a></blockquote> <script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>Of course, you can ask your prospective employers if it’s not clear what criteria you’ll be judged against. But unclear requirements lead to more time wasted, and more unnecessary stress. In the worst-case scenarios, the person doing the recruitment doesn’t know the criteria themselves, beyond “I’ll know it when I see it”. (If that’s the case, then it’s a major red flag.)</p>
<h2>What are the alternatives to a coding challenge?</h2>
<p>So, what do I advocate instead? How is a manager to know which candidates have the most potential to shine at a job?</p>
<h3>Ask pertinent questions (and trust in the answers)</h3>
<p>Well, naturally you can ask them. A good interviewer should be able to encourage prospective employees to talk about their past experience with different technologies in a way that gives them an insight into how they would fare in the job role. What have they built? Were they part of a team? What were the constraints they needed to consider? How did they come to certain technological decisions? What do they like or dislike about a given technology? Which areas do they feel comfortable in, and where do they feel their knowledge or experience is lacking? What are they curious about? What would they like to learn or improve upon?</p>
<p>People don’t generally lie about their experience (they would soon get found out), although sometimes they might exaggerate for their resumé. An example is an interviewer looking for a React developer. Someone listing “React” as a skill on their resumé could be an accomplished React developer, or they could have only surface-level knowledge gleaned from attending a bootcamp or walking through an online tutorial. It should only take a few minutes of talking to them to understand the depth of their knowledge in this area, and whether or not they might be a good candidate for the role.</p>
<h3>Have a probationary period</h3>
<p>Most jobs have this already – a fixed trial period (usually 6–12 weeks), where the employer and prospective employee can determine if they are a good fit for the role. If not, they are free to part ways. Coupled with a good interview process, this should be sufficient. Plenty of people don’t pass their probationary period despite completing a coding test or challenge. There are many factors other than technical proficiency that determine whether someone is a good fit for your team, but coding challenges often focus on technical proficiency above all else.</p>
<h3>Walk through their existing projects</h3>
<p>Most candidates will have side-projects or work they’ve done for previous employers. Ask them to show you the code and describe the process, asking questions along the way (see above).</p>
<p>Even candidates for a junior role, perhaps who are fresh from a bootcamp, will likely have projects they can show. If they’re experimental or not public, that’s ok. It’s not the point of the exercise.</p>
<p>There might be <em>some</em> cases where candidates are unable to show work for specific reasons (such as NDAs). In those cases it might still be possible to talk about the work and challenges faced, without viewing the source code or going into specifics. But if none of these things are practical, there are still a few more options...</p>
<h3>Ask them to build something – anything!</h3>
<p>Instead of a coding challenge, ask them to spend a short time building something <em>they</em> enjoy, and talk about what they did and why. Perhaps they want to take the time to learn about a specific technology, so they put together a simple demo that helps them understand it better. Maybe they’re already a wiz with CSS animation, so they make a fun demo that really shows off their skills. Or another option, experienced by <a href="https://twitter.com/TheJordanRules_">Jordan</a>:</p>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Just finished a technical interview... <br /><br />The process consisted of me implementing a new feature into one of my personal projects while the interviewer followed along<br /><br />I actually enjoyed this opposed to the typical technical interview question but I’m still nervous as shit 😭</p>— Jordan (@TheJordanRules_) <a href="https://twitter.com/TheJordanRules_/status/1301550883319226369?ref_src=twsrc%5Etfw">September 3, 2020</a></blockquote> <script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>The difference here is that candidates are likely to be more comfortable talking about something <em>they</em> understand. The point isn’t how polished the result is, it’s how they talk about it afterwards.</p>
<p>I would still exercise a note of caution with this, as you are still asking people to complete an assignment in their spare time, which is not an option for everyone. I don’t advocate it as standard, but could be something to consider in exceptional cases.</p>
<p>You could argue the point of a coding challenge is to test a candidate’s limitations, which you might not glean from allowing them to choose what to build. Testing a candidate’s limitations is a terrible idea, which can only enhance the stress of the situation. And, as previously stated, stressed people don’t give an accurate impression of how they would behave in a “real” work situation. (Unless your work environment is super stressful, which is a whole other problem...)</p>
<h3>Pair program</h3>
<p>Again, I’m on the fence about this one, because pair-programming is not necessarily the less-stressful option. Personally, I don’t feel like I do my best work when someone is watching my every move – I would probably do a lot less Googling, and write far worse code! But if this is done well and with empathy, it could be used as a last-resort.</p>
<h3>Focus on their ability to learn</h3>
<p>Arguably, a candidate’s ability to learn is more valuable than how much knowledge they possess on a given subject right now. Coding challenges can only really demonstrate a current skillset. A better strategy might be to walk through an example of your company or product’s code and encourage the candidate to ask questions, as well as talk about what aspects they are familiar with.</p>
<h2>A “good” coding challenge</h2>
<p>If I haven’t convinced you to dispense with coding challenges altogether, let me give you one final piece of advice: <strong>Don’t</strong> try to catch people out. Make your challenge something straightforward that people can complete without hidden “gotchas”.</p>
<p>Here’s an example: For one position, I was given the challenge to build a simple app using API calls to get and post data to a server. I was given access to a starter repository, only when I tried to run the project locally I kept getting an error. I spent hours trying to debug it, thinking I was doing something wrong, berating myself for falling at the first hurdle (before eventually figuring it out!). At the subsequent interview I learnt that this error had been put in <em>deliberately</em>, to test whether I would reach out and ask for help. This immediately put on the back foot for the rest of the interview, despite not remotely reflecting how I would act in a work environment.</p>
<p>So. Don’t do that. And, if you can help it, don’t set a coding challenge at all. There are better ways to spend your (and your candidates’) time.</p>
Favourite Things 1: GSAP ScrollTrigger, Eleventy, and more2020-08-31T00:00:00Zhttps://css-irl.info/favourite-things-01/<figure>
<img src="https://css-irl.info/favourite-things.svg" alt="A flat-colour line illustration of a bookmarked stack of folders" />
</figure>
<p>For a while I’ve been thinking about publishing a semi-regular round-up of all the things that have been interesting me in tech recently, partly with the aim of helping others discover new things. (Hey, that’s what this blog is for!) Kind of like a newsletter, without the newsletter part. Although maybe it’ll eventually become a newsletter too!</p>
<p>So here’s the first of what might become a regular update. If you enjoy this and would like to see more, let me know!</p>
<h2>GSAP ScrollTrigger plugin</h2>
<p>I’ve long been a fan of <a href="https://greensock.com/">Greensock Animation Platform</a> (GSAP) for web animation. Although I rarely have the opportunity to use it in my day-to-day work, it’s always an absolute joy when I do. It has a really straightforward and intuitive API for creating web animations ranging from the simple to the complex. I find it particularly useful for SVG animations, and the latest version seems to hail a vast improvement in both performance and ease-of-use.</p>
<p>I’d heard good things about the ScrollTrigger plugin, and seen some absolutely stunning demos that use it, like <a href="https://codepen.io/ste-vg/full/GRooLza">this one</a> from <a href="https://twitter.com/steeevg">Steve Gardner</a>, which is quite simply mindblowing. <a href="https://www.una-europa.eu/">This lovely site</a> built by <a href="https://twitter.com/Mamboleoo">Louis Hoebregts</a> and the <a href="https://twitter.com/Base_Design">Base Design</a> team is the one that finally prompted me to spend some time trying it out for myself.</p>
<figure>
<img src="https://css-irl.info/favourite-things-01.jpg" alt="Screenshot of Codepen demo with aeroplane wireframe in foreground" />
<figcaption>Steve Gardner’s awesome demo showcases the power of GSAP ScrollTrigger</figcaption>
</figure>
<p>Last year I wrote <a href="https://24ways.org/2019/beautiful-scrolling-experiences-without-libraries/">an article for 24ways</a> about using CSS scroll-snap and the Intersection Observer API to create scroll-based animations without the use of JS libraries. While the tips in the article are perfectly valid for relatively simple content and transitions, there comes a point in more complex sites where the power that a library gives you can be a massive boon to productivity. A few months ago I tried to build a reasonably complex interactive, animated scrolling page with vanilla JS, and it was definitely more of a headache than I anticipated. (The project was temporarily shelved for unrelated reasons, so I’ll spare you the link!) Had I known about the ScrollTrigger plugin back then, I definitely would’ve used it. Having since played around with it, the big difference is the ability to really <em>orchestrate</em> animations – I feel more like a conductor than a developer!</p>
<p>There is a bit of a learning curve involved, particularly if you’re new to GSAP. But investing a bit of time in it is completely worth it. I can’t wait to start using ScrollTrigger in real projects.</p>
<h2>Eleventy</h2>
<p>I’ve <a href="https://css-irl.info/from-gatsby-to-eleventy/">previously written</a> a bit about getting started with static site generator <a href="https://www.11ty.dev/">Eleventy</a>, and how it differs from Gatsby, which currently powers this blog. After becoming frustrated with the process of implementing a few new features in Gatsby, which seemed unnecessarily difficult, I decided to no longer delay migrating this blog onto Eleventy. I’ve made a start, getting the necessary project scaffolding in place, and expect to migrate the content and make the switch in the next few weeks – hopefully unveiling some new features at the same time!</p>
<p>Although not 100% straightforward (as with any new technology, there is a process of learning and getting to grips with best practices before getting into a comfortable groove), it already feels more like familiar territory to me. I think part of that has something to do with being closer to the “raw materials” of the web, while Gatsby (due to being React-based) feels a couple of layers away from that. It also allows me to use Nunjucks for templating, which I use day-to-day anyway. But it’s also no doubt partly due to my unfamiliarity with Node and GraphQL, which are particularly advantageous for doing any advanced development with Gatsby. If you’re comfortable with those, then Gatsby may well be better suited to your needs. I should also add, this isn’t the whole reason why using Gatsby doesn’t sit right with me. In recent weeks, some former employees/contractors have revealed a toxic work culture, which leaves a bitter taste for those of us wanting to support the “good” companies. Eleventy, with its supportive community of people who care about the web, feels like the “good” company.</p>
<h2>ThreeJS</h2>
<p>I’ve been snatching chunks of time here and there to try to learn <a href="https://threejs.org/">ThreeJS</a>. I’d love to be able to build 3D interactive scenes, and I have endless admiration for people like <a href="https://www.ilithya.rocks/">Ilithya</a> who make incredible things. So far I’ve only really got to grips with the basics. Thinking mathematically in three dimensions doesn’t come easily to me, but I’m pretty impressed with what you can do with even a basic grasp. I feel I need to spend a longer period of concentrated learning time to get beyond this, so thinking it’ll be more of a long term goal. My proudest accomplishment so far is turning my Atomic Smash colleagues into bouncing balls 😅</p>
<iframe height="411" style="width: 100%;" scrolling="no" title="ThreeJS Atomic Smash team" src="https://codepen.io/michellebarker/embed/oNxNKRE?height=411&theme-id=dark&default-tab=result" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/oNxNKRE">ThreeJS Atomic Smash team</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<h2>Article: My Tech-Savvy Privilege</h2>
<p>Every so often an article comes along that sticks in my head in a way that I can’t ignore. Right now it’s <a href="https://ohhelloana.blog/my-tech-savvy-privilege/">this insightful article</a> by <a href="https://twitter.com/ohhelloana">Ana Rodrigues</a>. Ana is a wonderful writer who writes with such empathy about technology. In this article she eloquently points out how far removed we are, in our technically proficient bubble, from a large proportion of the real people who use our products. We simply have no understanding of what a frustrating (and sometimes debilitating) experience we are creating for some people. I often think about this when I encounter a problem with a service and cannot find a support phone number. If I, as a technically adept person, am finding this difficult, imagine that difficulty multiplied for those less familiar with what we regard as digital conventions. Ana’s article also reminds me a little of one I wrote for the <a href="https://the-pastry-box-project.net/michelle-barker/2015-march-4">Pastry Box Project</a> a few years ago, when I was thinking about these things. Sadly, it seems not much has changed, and in some ways we’ve put up even greater barriers.</p>
<h2>New CSS property: content-visibility</h2>
<p>Want to improve your site’s performance with relatively little effort? The new CSS <code>content-visibility</code> property looks pretty exciting! It enables the browser to delay rendering an element until it’s needed, thus cutting load time and allowing faster interaction with on-screen content. <a href="https://twitter.com/una">Una Kravets</a> and <a href="https://web.dev/authors/vladimirlevin/">Vladimir Levin</a> break it down <a href="https://web.dev/content-visibility/">in this article</a>. It’s only supported in Chrome right now, but let’s face it, that’s a large enough proportion of users to make it worthwhile using already.</p>
Building the Zig-Zag Gradient Lab2020-08-13T00:00:00Zhttps://css-irl.info/building-the-zig-zag-gradient-lab/<figure>
<img src="https://css-irl.info/building-the-zig-zag-gradient-lab-01.jpg" alt="Opening slide from the talk, titled Building the Zig-Zag Gradient Lab, with colourful zig-zag background" />
</figure>
<p>Last month I had the priviledge of giving a talk at <a href="https://webclerks.at/vienna-calling/">Vienna Calling</a>, the online meetup organised by <a href="https://twitter.com/CssVienna">CSS-In-Vienna</a> and <a href="https://twitter.com/wearewebclerks">Webclerks</a>. Alongside fantastic talks by <a href="https://www.youtube.com/watch?v=rGjh0EFxFuQ&list=PLSJe-hizqRL0qMDlLzBp1WZZXJFdmP6lz&index=2">Cassie</a>, <a href="https://www.youtube.com/watch?v=8oMekThCB9k&list=PLSJe-hizqRL0qMDlLzBp1WZZXJFdmP6lz&index=6">Andy</a>, <a href="https://www.youtube.com/watch?v=tux47Pwaar4&list=PLSJe-hizqRL0qMDlLzBp1WZZXJFdmP6lz&index=4">Ramón</a> and <a href="https://www.youtube.com/watch?v=4SOF7ARvoug&list=PLSJe-hizqRL0qMDlLzBp1WZZXJFdmP6lz&index=5">Carie</a>, I spoke about how I built a recent Codepen demo, the Zig-Zag Gradient Lab:</p>
<iframe height="471" style="width: 100%;" scrolling="no" title="Zig-zag gradient lab" src="https://codepen.io/michellebarker/embed/abdKLLz?height=471&theme-id=dark&default-tab=result" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/abdKLLz">Zig-zag gradient lab</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>You can watch the full talk <a href="https://www.youtube.com/watch?v=-7t1pg0ENLY&list=PLSJe-hizqRL0qMDlLzBp1WZZXJFdmP6lz&index=3">here</a>, or read the transcript (including some slides and code examples) below.</p>
<p>https://www.youtube.com/embed/-7t1pg0ENLY</p>
<h2>Transcript</h2>
<p>Hi, I’m Michelle, I’m a front end developer, and my main hobby is messing about with CSS on Codepen and creating pointless demos. I assume that most of you know what Codepen is, but if you don’t, it’s a front end playground where you can go all out making fun things without worrying about clients, or browser support, all the things we love about our jobs. Today I want to tell you the story of how I created a recent Codepen demo...and it all starts with gradients.</p>
<p>A lot of people have been redesigning their personal websites recently. Cassie is one of them – if you haven’t seen her personal site, it’s incredible, go and check it out. And this inspired me, I started thinking about ways I could redesign my personal site and bring a bit more creativity, a bit more personality. And one of the ways I thought about doing this was using background gradients in CSS to make some geometric patterns.</p>
<p>You might already know that you can create clean lines with CSS gradients by using hard colour stops. So if we want to transition between one colour and another without a fade you can use two consecutive colour stops with the same value. (Fig 01)</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.element</span> <span class="token punctuation">{</span>
<span class="token property">background</span><span class="token punctuation">:</span> <span class="token function">linear-gradient</span><span class="token punctuation">(</span>45deg<span class="token punctuation">,</span> darkorchid 50%<span class="token punctuation">,</span> turquoise 50%<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<figure>
<img src="https://css-irl.info/building-the-zig-zag-gradient-lab-01a.jpg" alt="A 45 degree diagonal gradient" />
<figcaption><em>Fig 01</em> Gradient with consecutive colour stops</figcaption>
</figure>
<p>If we set the <code>background-size</code> to a fixed value, then that gradient pattern is going to repeat, and cover our background. (Fig 02)</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.element</span> <span class="token punctuation">{</span>
<span class="token property">background</span><span class="token punctuation">:</span> <span class="token function">linear-gradient</span><span class="token punctuation">(</span>45deg<span class="token punctuation">,</span> darkorchid 50%<span class="token punctuation">,</span> turquoise 50%<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">background-size</span><span class="token punctuation">:</span> 5rem 5rem<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<figure>
<img src="https://css-irl.info/building-the-zig-zag-gradient-lab-02.jpg" alt="A two-tone repeating gradient which creates a triangle pattern" />
<figcaption><em>Fig 02</em> Gradient with fixed background size</figcaption>
</figure>
<p>We could do a similar thing with radial gradients to created a spotted pattern. Or we could overlay multiple backgrounds [to make more complex patterns]. This demo uses multiple gradient backgrounds:</p>
<iframe height="427" style="width: 100%;" scrolling="no" title="Multiple CSS gradient example" src="https://codepen.io/michellebarker/embed/wvMYZdN?height=427&theme-id=dark&default-tab=result" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/wvMYZdN">Multiple CSS gradient example</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>So you can get really creative and make some cool patterns just with gradients.</p>
<h3>Repeating gradients</h3>
<p>Using repeating gradients also allow us to produce some really cool effects. So we could have a <a href="https://codepen.io/michellebarker/pen/YzwJMOj">striped background</a>, with three colours, which just repeats, we don’t have to set every stripe.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.element</span> <span class="token punctuation">{</span>
<span class="token property">background</span><span class="token punctuation">:</span> <span class="token function">repeating-linear-gradient</span><span class="token punctuation">(</span>
45deg<span class="token punctuation">,</span>
darkorchid<span class="token punctuation">,</span>
darkorchid 40px<span class="token punctuation">,</span>
turquoise 40px<span class="token punctuation">,</span>
turquoise 80px<span class="token punctuation">,</span>
chartreuse 80px<span class="token punctuation">,</span>
chartreuse 120px
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Or we could use <code>repeating-radial-gradient</code>, which gives us <a href="https://codepen.io/michellebarker/pen/eYJPoxg">these concentric circles</a>.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.element</span> <span class="token punctuation">{</span>
<span class="token property">background</span><span class="token punctuation">:</span> <span class="token function">repeating-radial-gradient</span><span class="token punctuation">(</span>
circle at center<span class="token punctuation">,</span>
darkorchid<span class="token punctuation">,</span>
darkorchid 40px<span class="token punctuation">,</span>
turquoise 40px<span class="token punctuation">,</span>
turquoise 80px<span class="token punctuation">,</span>
chartreuse 80px<span class="token punctuation">,</span>
chartreuse 120px
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Setting a fixed size again gives us some pretty cool effects – our stripes join together, and we get this interesting pattern. (Fig 03)</p>
<figure>
<img src="https://css-irl.info/building-the-zig-zag-gradient-lab-03.jpg" alt="Concentric circles fusing with one another in psychedelic colours" />
<figcaption><em>Fig 03</em> Repeating radial gradients with fixed background size</figcaption>
</figure>
<p>And <a href="https://codepen.io/michellebarker/pen/ExPdJqb">different variations</a> in the background size and the thickness of the stripes produce different patterns. (Fig 04)</p>
<figure>
<img src="https://css-irl.info/building-the-zig-zag-gradient-lab-04.jpg" alt="Different variations of the concentric circle pattern" />
<figcaption><em>Fig 04</em> Adjusting the different property values gives us very different results.</figcaption>
</figure>
<p>So far so good, there are lots of cool ways I can create geometric patterned backgrounds for my personal site. But there’s one thing I’m really good at, and that’s procrastination! I still haven’t even started my personal site. But I thought to myself, “I’d quite like to create a zig-zag background pattern”. And I figured someone, somewhere has already done this, but this is a learning exercise – I wanted to work out how <em>I</em> would do it, and then see if there was a better way. So how would I do it?</p>
<h3>Creating a zig-zag gradient pattern</h3>
<p>I started off creating a striped background with a 45 degree angle and a fixed background size, so we get this kind of candy stripe pattern. (Fig 05)</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.element</span> <span class="token punctuation">{</span>
<span class="token property">background</span><span class="token punctuation">:</span> <span class="token function">repeating-linear-gradient</span><span class="token punctuation">(</span>
45deg<span class="token punctuation">,</span>
darkorchid<span class="token punctuation">,</span>
darkorchid 40px<span class="token punctuation">,</span>
turquoise 40px<span class="token punctuation">,</span>
turquoise 80px<span class="token punctuation">,</span>
chartreuse 80px<span class="token punctuation">,</span>
chartreuse 120px
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">background-size</span><span class="token punctuation">:</span> 100px 100%<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<figure>
<img src="https://css-irl.info/building-the-zig-zag-gradient-lab-05.jpg" alt="Repeating striped background" />
<figcaption><em>Fig 05</em> Striped background with repeating linear gradient and fixed size</figcaption>
</figure>
<p>Then I used an absolute-positioned pseudo-element with the same background at the opposite angle. (Fig 06)</p>
<figure>
<img src="https://css-irl.info/building-the-zig-zag-gradient-lab-06.jpg" alt="The previous gradient with the opposite angle (-45 degrees)" />
<figcaption><em>Fig 06</em></figcaption>
</figure>
<p>Then I used the <code>mask-image</code> property with a repeating linear gradient to mask the pseudo element with vertical stripes,so the original element is visible through the pseudo element. I’ve left a bit of an overlay [in this image] so you can see where those hidden stripes would be. (Fig 07)</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.element</span> <span class="token punctuation">{</span>
<span class="token property">mask-image</span><span class="token punctuation">:</span> <span class="token function">repeating-linear-gradient</span><span class="token punctuation">(</span>
to right<span class="token punctuation">,</span>
black 100px<span class="token punctuation">,</span>
black 200px<span class="token punctuation">,</span>
transparent 200px<span class="token punctuation">,</span>
transparent 300px
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<figure>
<img src="https://css-irl.info/building-the-zig-zag-gradient-lab-07.jpg" alt="Showing the areas revealed by the mask-image property" />
<figcaption><em>Fig 07</em> The two gradients overlaid. The semi-transparent areas show where the pattern below will be visible.</figcaption>
</figure>
<p>This is the actual result. (Fig 08)</p>
<figure>
<img src="https://css-irl.info/building-the-zig-zag-gradient-lab-08.jpg" alt="Zig-zag striped gradient pattern" />
<figcaption><em>Fig 08</em> The resulting zig-zag pattern</figcaption>
</figure>
<h3>Alternative solutions</h3>
<p>Now, I don’t completely love this solution, as is uses a pseudo-element – I kind of wanted this to be possible with a single element background. I did a bit of searching to see if anyone had a solution that was better than mine, and one of the results that came up was Lea Verou’s <a href="https://leaverou.github.io/css3patterns/">CSS Patterns Gallery</a>, from a few years back. Of course, if anyone has a solution to this, it’s Lea Verou. And there <em>is</em> a zig-zag pattern on the site, but with one crucial difference: it’s only two colours. I wanted my pattern to be three or more colours. I searched a bit more, but I couldn’t find an example anywhere that was more than two colours. That’s not to say it isn’t possible, but figuring it out was a bit more than my tiny brain can handle. So I decided to stick with my solution for now.</p>
<h3>Custom properties</h3>
<p>So, once I had my zig-zag gradient, I wanted to experiment a bit with colours, the thickness of the stripes, and the size of the zig-zags. Enter <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Variables">custom properties</a>. CSS custom properties are among my favourite things in CSS – I use them in so many places. They are perfect for when you have a value repeating in a number of places, and you just want to tweak that value and have it update everywhere. So I can replace the gradient stops with a custom property, which represents the thickness of the stripes, and another that represents the background width. I need to do this in the pseudo-element too, but for brevity I’m not showing all the code here.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.element</span> <span class="token punctuation">{</span>
<span class="token property">--t</span><span class="token punctuation">:</span> 40px<span class="token punctuation">;</span>
<span class="token property">--w</span><span class="token punctuation">:</span> 100px<span class="token punctuation">;</span>
<span class="token property">background</span><span class="token punctuation">:</span> <span class="token function">repeating-linear-gradient</span><span class="token punctuation">(</span>
45deg<span class="token punctuation">,</span>
darkorchid<span class="token punctuation">,</span>
darkorchid <span class="token function">var</span><span class="token punctuation">(</span>--t<span class="token punctuation">)</span><span class="token punctuation">,</span>
turquoise <span class="token function">var</span><span class="token punctuation">(</span>--t<span class="token punctuation">)</span><span class="token punctuation">,</span>
turquoise <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--t<span class="token punctuation">)</span> * 2<span class="token punctuation">)</span><span class="token punctuation">,</span>
chartreuse <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--t<span class="token punctuation">)</span> * 2<span class="token punctuation">)</span><span class="token punctuation">,</span>
chartreuse <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--t<span class="token punctuation">)</span> * 3<span class="token punctuation">)</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">background-size</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--w<span class="token punctuation">)</span> 100%<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>I can just update that value to get different outcomes without changing anything else in the code. (Fig 9)</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.element</span> <span class="token punctuation">{</span>
<span class="token property">--t</span><span class="token punctuation">:</span> 20px<span class="token punctuation">;</span>
<span class="token property">--w</span><span class="token punctuation">:</span> 120px<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<figure>
<img src="https://css-irl.info/building-the-zig-zag-gradient-lab-09.jpg" alt="Slide with a code example, showing a zig-zag background with thinner, wider stripes" />
<figcaption><em>Fig 09</em> Changing the custom properties changes the appearance of the zig-zag stripes</figcaption>
</figure>
<p>And we can replace some other values with custom properties too. We can adjust the angle of the zig-zags [for example], by tweaking a single custom property.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.element</span> <span class="token punctuation">{</span>
<span class="token property">--t</span><span class="token punctuation">:</span> 40px<span class="token punctuation">;</span>
<span class="token property">--w</span><span class="token punctuation">:</span> 40px<span class="token punctuation">;</span>
<span class="token property">--angle</span><span class="token punctuation">:</span> 40deg<span class="token punctuation">;</span>
<span class="token property">background</span><span class="token punctuation">:</span> <span class="token function">repeating-linear-gradient</span><span class="token punctuation">(</span>
<span class="token function">var</span><span class="token punctuation">(</span>--angle<span class="token punctuation">)</span><span class="token punctuation">,</span>
darkorchid<span class="token punctuation">,</span>
darkorchid <span class="token function">var</span><span class="token punctuation">(</span>--t<span class="token punctuation">)</span><span class="token punctuation">,</span>
turquoise <span class="token function">var</span><span class="token punctuation">(</span>--t<span class="token punctuation">)</span><span class="token punctuation">,</span>
turquoise <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--t<span class="token punctuation">)</span> * 2<span class="token punctuation">)</span><span class="token punctuation">,</span>
chartreuse <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--t<span class="token punctuation">)</span> * 2<span class="token punctuation">)</span><span class="token punctuation">,</span>
chartreuse <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--t<span class="token punctuation">)</span> * 3<span class="token punctuation">)</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">background-size</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--w<span class="token punctuation">)</span> 100%<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>These values are needed for the initial element’s background <em>and</em> the pseudo element, but by setting a custom property I only need to change it in one place, because the pseudo element will inherit the new value.</p>
<p>So we can update those custom properties and have an infinite number of variations, but what would be even cooler than that? If we could edit those values with a GUI and have our gradient pattern change before our eyes.</p>
<p>Well, if we add some inputs and a bit of Javascript, then we can! With these range sliders, I can update the custom property every time the value changes (<a href="https://codepen.io/michellebarker/pen/abdKLLz">see demo</a>). This is ok, because the sliders have a step increment of 1, but the change is not that smooth because unfortunately gradients are not animatable within CSS.</p>
<h2>Houdini</h2>
<p>But, not so fast! We <em>can</em> animate custom properties using Houdini. I always struggle to explain exactly what Houdini is, so I’m going to quote right off <a href="https://developer.mozilla.org/en-US/docs/Glossary/Houdini">MDN</a>:</p>
<blockquote>
<p>Houdini is a set of low-level APIs that exposes parts of the CSS engine, giving developers the power to extend CSS by hooking into the styling and layout process of a browser’s rendering engine</p>
</blockquote>
<p>Houdini consists of a bunch of APIs, but the one that’s important for us here is the Properties & Values API. That allows us to register the property in Javascript and then animate or transition that custom property in exactly the same way you would a normal animatable CSS property.</p>
<pre class="language-css"><code class="language-css"><span class="token property">transition</span><span class="token punctuation">:</span> --angle 200ms<span class="token punctuation">,</span> --t 200ms<span class="token punctuation">,</span> --w 200ms<span class="token punctuation">;</span></code></pre>
<p>And that makes that gradient transition much smoother. Support is currently limited to Chromium browsers, so it’s not going to work in Firefox or Safari. <a href="https://ishoudinireadyyet.com/">This website</a> tells you how well supported the different APIs are. But in Chrome, the gradient transition is just that little bit nicer.</p>
<h3>Colour</h3>
<p>There’s one part that I haven’t really covered yet, and that’s the colour. I <em>could</em> have three colour inputs and allow users to fully customise the colour of the gradient stripes. But I quite liked the idea of users being able to adjust the colour via a slider, rather than dropdown or colour pickers, that seemed a little clunky.</p>
<p>I like to use HSL when I’m working with custom properties and colour. HSL stands for Hue Saturation Lightness. HSL is really nice to work with using custom properties because it allows you to tweak colour values really easily. In the gradient lab I’m using a custom property for the <code>hue</code> value – the first value in the HSL function:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.element</span> <span class="token punctuation">{</span>
<span class="token property">--color1</span><span class="token punctuation">:</span> <span class="token function">hsl</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--h1<span class="token punctuation">)</span><span class="token punctuation">,</span> 90%<span class="token punctuation">,</span> 50%<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>The user can select the primary hue using the slider, and the two other hues are calculated as adjacent colours from the opposite side of the colour wheel. (Fig 10)</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.element</span> <span class="token punctuation">{</span>
<span class="token property">--h1</span><span class="token punctuation">:</span> 0deg<span class="token punctuation">;</span>
<span class="token property">--h2</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--h1<span class="token punctuation">)</span> + <span class="token function">calc</span><span class="token punctuation">(</span>180deg - 30deg<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">--h3</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--h1<span class="token punctuation">)</span> + <span class="token function">calc</span><span class="token punctuation">(</span>180deg + 30deg<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">--color1</span><span class="token punctuation">:</span> <span class="token function">hsl</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--h1<span class="token punctuation">)</span><span class="token punctuation">,</span> 90%<span class="token punctuation">,</span> 50%<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">--color2</span><span class="token punctuation">:</span> <span class="token function">hsl</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--h2<span class="token punctuation">)</span><span class="token punctuation">,</span> 90%<span class="token punctuation">,</span> <span class="token function">var</span><span class="token punctuation">(</span>--l<span class="token punctuation">,</span> 70%<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">--color3</span><span class="token punctuation">:</span> <span class="token function">hsl</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--h3<span class="token punctuation">)</span><span class="token punctuation">,</span> 60%<span class="token punctuation">,</span> <span class="token function">var</span><span class="token punctuation">(</span>--d<span class="token punctuation">,</span> 40%<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<figure>
<img src="https://css-irl.info/building-the-zig-zag-gradient-lab-10.jpg" alt="Colour wheel illustration showing how the hues are calculated" />
<figcaption><em>Fig 10</em> Split complementary colour scheme with custom properties</figcaption>
</figure>
<p>That’s known as a split complementary colour scheme. So if the user changes the primary hue using the slider, those other two colours are adjusted accordingly.</p>
<p>Incidentally, the <a href="https://codepen.io/michellebarker/pen/PoZVqYd">colour wheel illustration</a> is built with CSS gradients and custom properties too!</p>
<h3>Storing the values</h3>
<p>Lastly, it would be pretty useful if, once we have a gradient we like, it didn’t reset the moment we navigate away from the page, or close our browser window. We can use <code>localStorage</code> to store the values locally in the user’s browser:</p>
<pre class="language-js"><code class="language-js">localStorage<span class="token punctuation">.</span><span class="token function">setItem</span><span class="token punctuation">(</span><span class="token string">'angle'</span><span class="token punctuation">,</span> <span class="token string">'45'</span><span class="token punctuation">)</span></code></pre>
<p>Then retrieve them and apply them as soon as the page is loaded:</p>
<pre class="language-js"><code class="language-js">localStorage<span class="token punctuation">.</span><span class="token function">getItem</span><span class="token punctuation">(</span><span class="token string">'angle'</span><span class="token punctuation">)</span></code></pre>
<p>So that’s the story of how I made this little tool. I don’t know if anyone will use it for anything real, but it was fun to make and has given me lots of inspiration for my personal site.</p>
<h2>Reference</h2>
<ul>
<li><a href="https://codepen.io/michellebarker/pen/abdKLLz">Full demo</a></li>
<li><a href="https://codepen.io/collection/XJmqZe">A Codepen collection of all the demos featured in this talk</a></li>
</ul>
Accessibly Hiding Focus Outlines2020-08-09T00:00:00Zhttps://css-irl.info/accessibly-hiding-focus-outlines/<p><strong>Update (January 2023): This article was written before <code>:focus-visible</code> was widely supported. Browsers have since implemented <code>:focus-visible</code> as the default for displaying the focus outline. I no longer recommend the solution detailed in this article.</strong></p>
<p>By default, browsers provide styling for elements when they receive focus. This varies from browser to browser, but is typically an outline around an element. In Chrome, the focus style manifests as a blue glowing outline. In Firefox it’s a thin dotted outline that inherits the element’s colour.</p>
<p>The focus ring (as it’s commonly known) should be regarded as a feature, not a bug. It’s especially useful if you’re navigating a website using a keyboard rather than a mouse: when tabbing through elements, the user can see exactly where they are on a webpage, and which element is currently in focus. <a href="https://css-tricks.com/focusing-on-focus-styles/">This article by Eric Bailey</a> does a great job of explaining why focus styles are important. Unfortunately, its benefits are often poorly understood by clients and designers. Developers are frequently asked to remove the focus outline by others who believe it to be unintentional, or unsightly. Personally, I’ve fought this battle many time, and not always successfully.</p>
<h2>Styling with CSS</h2>
<p>If you’re forced to hide the browser’s focus ring, the least you can do is provide an alternative focus style. We can use CSS to provide focus styles that are more in keeping with your website’s brand. We can style these using the <code>:focus</code> pseudo class, like so:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">input:focus</span> <span class="token punctuation">{</span>
<span class="token property">outline</span><span class="token punctuation">:</span> 2px solid deeppink<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>(Eric’s article also goes into additional options for styling, like <code>:focus-within</code>.) But that often relies on a designer providing focus styles that are <em>at least</em> as accessible as the browser defaults (e.g. they provide a greater visible change than simply a change of colour, which people with colour blindness or other visual impairments may have trouble perceiving), and sometimes require a client to signoff on them. Sometimes there is an opportunity to back to the designer to request accessible focus styles, but other times you’re not in a position to delay the project.</p>
<h2>An accessible alternative with Javascript</h2>
<p>Recently, my colleague <a href="https://twitter.com/codekipple">Carl Hughes</a> pointed me to a solution from Spotify developer <a href="https://jmperezperez.com/">José M. Perez</a>, which uses Javascript to hide the focus ring initially, then display it only when a user interacts with a key press, by appending a class to the document root:</p>
<pre class="language-js"><code class="language-js">document<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'keyup'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">event</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment">/* if key is tab */</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>event<span class="token punctuation">.</span>which <span class="token operator">===</span> <span class="token number">9</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
document<span class="token punctuation">.</span>documentElement<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span><span class="token string">'u-no-focus-outline'</span><span class="token punctuation">)</span>
document<span class="token punctuation">.</span>documentElement<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">'u-focus-outline'</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<p>Then we can use CSS to hide the outline is the document has the “no focus” class.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.u-no-focus-outline *:focus</span> <span class="token punctuation">{</span>
<span class="token property">outline</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Of course, you might not want to remove the focus ring on <em>every</em> type of element, and you can style specific elements according to preference.</p>
<h3>Improving the accessibility</h3>
<p>My colleague modified the code slightly so that the class is added with JS. That way the website will still be accessible if JS fails to load, as the focus styles will remain intact. Additionally, I made a couple of small changes: Switching the deprecated <code>event.which</code> property to <code>event.code</code>, and appending the class to the <code><body></code> tag instead of the document root (which is just a personal preference):</p>
<pre class="language-js"><code class="language-js">document<span class="token punctuation">.</span>body<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">'u-no-focus-outline'</span><span class="token punctuation">)</span>
document<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">'keyup'</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">event</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>event<span class="token punctuation">.</span>code <span class="token operator">===</span> <span class="token number">9</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
document<span class="token punctuation">.</span>body<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">remove</span><span class="token punctuation">(</span><span class="token string">'u-no-focus-outline'</span><span class="token punctuation">)</span>
document<span class="token punctuation">.</span>body<span class="token punctuation">.</span>classList<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">'u-focus-outline'</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<p>This solution seems to be simple, elegant, and satisfies the client’s requests, while maintaining accessibility. I’m almost annoyed I never thought of it! It can be coupled with custom CSS styling for your focus states too, so that everyone gets a good experience that’s in keeping with the brand. There may be other issues I haven’t considered, so if you’ve come across any pitfalls with this method I’d love to hear about them. Otherwise, I can see no reason not to use it in production.</p>
<p><a href="https://jmperezperez.com/outline-focus-ring-a11y/">Read the original blog post here.</a></p>
<h2>Future styling with <code>:focus-visible</code></h2>
<p>The <code>:focus-visible</code> <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:focus-visible">CSS pseudo class</a> is, in fact, designed to solve this very problem. We can use it to target only elements that are focused with a keyboard. Unfortunately, browser support is currently <a href="https://caniuse.com/#search=focus-visible">very poor</a>. As Eric’s article points out, there is a <a href="https://github.com/WICG/focus-visible">polyfill</a>, but I would personally favour the above solution over the extra development overhead that a polyfill adds.</p>
<p>Finally, I would like to point out that focus styles can be very useful in a number of ways that may not have been considered by a designer or client, even for mouse users, and removing them still might not be the best idea. From the article:</p>
<blockquote>
<p>Another point to consider is that focus styles can be desirable for mouse users. Their presence is a clear and unambiguous indication of interactivity, which is a great affordance for people with low vision conditions, cognitive concerns, and people who are less technologically adept.</p>
</blockquote>
<p>Personally, I find it incredibly useful to have clear focus styles on form inputs, which makes is very obvious which field you’re currently filling in. So think twice before you remove those focus outlines – the browser adds them for a reason!</p>
<h2>Resources</h2>
<ul>
<li><a href="https://developer.paciellogroup.com/blog/2018/03/focus-visible-and-backwards-compatibility/">:focus-visible and backwards compatibility</a> by Patrick H Lauke</li>
<li><a href="https://css-tricks.com/focusing-on-focus-styles/">Focusing on Focus Styles </a> by Eric Bailey</li>
<li><a href="https://www.matuzo.at/blog/testing-with-tab/">One of my favourite accessibility testing tools: The Tab Key</a> by Manual Matuzovic</li>
</ul>
Drop-Shadow: The Underrated CSS Filter2020-08-04T00:00:00Zhttps://css-irl.info/drop-shadow-the-underrated-css-filter/<p><strong>This article was updated on 13 August 2020 to include additional reference material.</strong></p>
<p>If you’re familiar with CSS, you probably know all about the <code>box-shadow</code> property. But did you know there is a CSS filter, <code>drop-shadow</code>, that <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/filter-function/drop-shadow">does something similar</a>? Like <code>box-shadow</code>, we can pass in values for x-offset, y-offset, blur radius and colour:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.my-element</span> <span class="token punctuation">{</span>
<span class="token property">filter</span><span class="token punctuation">:</span> <span class="token function">drop-shadow</span><span class="token punctuation">(</span>0 0.2rem 0.25rem <span class="token function">rgba</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 0.2<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Unlike <code>box-shadow</code>, it doesn’t take a <code>spread</code> parameter (more on that later).</p>
<h2>Why is drop-shadow useful?</h2>
<p>If we have <code>box-shadow</code>, why do we need <code>drop-shadow</code> at all?</p>
<h3>Non-rectangular shapes</h3>
<p>Using <code>drop-shadow</code> allows us to add a shadow to an element that doesn’t correspond to its bounding box, but instead uses the element’s alpha mask. We could add a drop shadow to a transparent PNG or SVG logo, for instance.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">img</span> <span class="token punctuation">{</span>
<span class="token property">filter</span><span class="token punctuation">:</span> <span class="token function">drop-shadow</span><span class="token punctuation">(</span>0.35rem 0.35rem 0.4rem <span class="token function">rgba</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 0.5<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>We can compare the effect of <code>box-shadow</code> versus <code>drop-shadow</code>:</p>
<figure>
<img src="https://css-irl.info/drop-shadow-01.jpg" alt="A pink cat logo with box-shadow on the left and the same logo with drop-shadow on the right" />
</figure>
<p>Using <code>box-shadow</code> gives us a rectangular shadow, even though the element has no background, while <code>drop-shadow</code> creates a shadow of the non-transparent parts of the image.</p>
<p><a href="https://codepen.io/michellebarker/pen/RwrXXby">Demo</a></p>
<p>This will work whether the image is inline in the HTML (either as an inline SVG, or in <code><img></code> tag), or a CSS background image. That means we could also add a shadow to a gradient background. These shapes are created with background gradients, with the <code>drop-shadow</code> filter applied:</p>
<iframe height="414" style="width: 100%;" scrolling="no" title="Gradient shapes with drop-shadow" src="https://codepen.io/michellebarker/embed/RwrXXaB?height=414&theme-id=dark&default-tab=result" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/RwrXXaB">Gradient shapes with drop-shadow</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<h3>Clipped elements</h3>
<p>If we clip or mask an element using <code>clip-path</code> or <code>mask-image</code>, any <code>box-shadow</code> we add will be clipped too - so it will be invisible if it’s outside of the clipped area.</p>
<p>But we can create a drop shadow on the clipped element by applying the <code>drop-shadow</code> filter on the element’s parent. Pretty cool!</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.parent-element</span> <span class="token punctuation">{</span>
<span class="token property">filter</span><span class="token punctuation">:</span> <span class="token function">drop-shadow</span><span class="token punctuation">(</span>0.35rem 0.35rem 0.4rem <span class="token function">rgba</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 0.5<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.clipped-element</span> <span class="token punctuation">{</span>
<span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>0 0<span class="token punctuation">,</span> 50% 0<span class="token punctuation">,</span> 100% 50%<span class="token punctuation">,</span> 50% 100%<span class="token punctuation">,</span> 0 100%<span class="token punctuation">,</span> <span class="token punctuation">,</span> 50% 50%<span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span></code></pre>
<figure>
<img src="https://css-irl.info/drop-shadow-02.jpg" alt="Pink chevron shape with blue drop-shadow" />
<figcaption>The drop-shadow filter is applied on the parent element of the clipped shape.</figcaption>
</figure>
<p><a href="https://codepen.io/michellebarker/pen/PoNYwvY">See the demo</a></p>
<h3>Grouped elements</h3>
<p>On occasion I’ve needed to build components made up of overlapping elements, which itself needs to cast a shadow.</p>
<p>If we add a <code>box-shadow</code> to the whole component, we’ll be left with strange empty spaces:</p>
<figure>
<img src="https://css-irl.info/drop-shadow-03.jpg" alt="Two call-to-action components with box-shadow" />
<figcaption>Box-shadow applied to component</figcaption>
</figure>
<p>If we add a <code>box-shadow</code> to each element individually, then each one will cast its own shadow, which might not be the desired effect. We’d need to employ some clever CSS to hide those shadows where elements overlap.</p>
<figure>
<img src="https://css-irl.info/drop-shadow-04.jpg" alt="Two call-to-action components with box-shadow" />
<figcaption>Box-shadow applied to columns</figcaption>
</figure>
<p>But by using <code>drop-shadow</code> on the whole component, we get the shadow exactly where we want it, without resorting to hacks:</p>
<figure>
<img src="https://css-irl.info/drop-shadow-05.jpg" alt="Two call-to-action components with drop-shadow" />
<figcaption>Drop-shadow applied to component</figcaption>
</figure>
<p><a href="https://codepen.io/michellebarker/pen/poyogzm">See the demo</a></p>
<h2>Multiple drop shadows</h2>
<p>Here’s a fun thing: You can use multiple drop shadows for some pretty cool effects! Check out the following demo.</p>
<iframe height="498" style="width: 100%;" scrolling="no" title="Multiple drop-shadows on clipped element" src="https://codepen.io/michellebarker/embed/MWygYdm?height=498&theme-id=dark&default-tab=result" frameborder="no" loading="lazy" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/MWygYdm">Multiple drop-shadows on clipped element</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>(Side note: transitioning and animating CSS filters isn’t particularly performant, and it’s probably best to avoid animating this many filters at once in real projects. This one’s just for fun though!)</p>
<h2>Limitations</h2>
<p>As mentioned above, <code>drop-shadow</code> doesn’t include the <code>spread</code> parameter. This means we can’t currently use it to create an outline effect, which I think would be really useful. For example, if it was supported, we could use <code>drop-shadow</code> to create an outline on a gradient background, in the way we can with <code>box-shadow</code> on other elements.</p>
<h2>Gotchas</h2>
<p><code>drop-shadow</code> doesn’t render the exact same shadow effect as <code>box-shadow</code>, even when given the same parameters. <code>box-shadow</code> tends to give a darker, heavier shadow than <code>drop-shadow</code> when the same values are used. I suspect this is something to do with CSS filters being based on SVG filter primitives. Whatever the case, you’ll likely need to compensate for the difference by adjusting your <code>drop-shadow</code> values somewhat.</p>
<aside>
<p>If you’re interested in further reading, <a href="https://twitter.com/anatudor">Ana Tudor</a> pointed me to <a href="https://dbaron.org/log/20110225-blur-radius">this article</a> on how blur radius is calculated.</p>
</aside>
<h2>Browser support</h2>
<p>CSS filters (including <code>drop-shadow</code>) are supported in all modern browsers. I tend to use it as progressive enhancement, without the need for a workaround for older browsers, as it isn’t normally something that would affect the user experience in any significant way. But if you do need to provide alternative styling for older browsers, you could do so using a feature query, with a <code>box-shadow</code> fallback:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.my-element > *</span> <span class="token punctuation">{</span>
<span class="token property">box-shadow</span><span class="token punctuation">:</span> 0 0.2rem 0.25rem <span class="token function">rgba</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 0.2<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token atrule"><span class="token rule">@supports</span> <span class="token punctuation">(</span><span class="token property">filter</span><span class="token punctuation">:</span> <span class="token function">drop-shadow</span><span class="token punctuation">(</span>0 0.2rem 0.25rem <span class="token function">rgba</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 0.2<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token selector">.my-element</span> <span class="token punctuation">{</span>
<span class="token property">filter</span><span class="token punctuation">:</span> <span class="token function">drop-shadow</span><span class="token punctuation">(</span>0 0.2rem 0.25rem <span class="token function">rgba</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 0.2<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.my-element > *</span> <span class="token punctuation">{</span>
<span class="token property">box-shadow</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<h2>Conclusion</h2>
<p>Despite having excellent support, the <code>drop-shadow</code> filter is highly under-utilised. I hope this article highlights some cases where it could save you hacking around with <code>box-shadow</code> – maybe you could use it in your next project!</p>
From Gatsby to Eleventy: Choosing a Static Site Generator for a Personal Site2020-07-15T00:00:00Zhttps://css-irl.info/from-gatsby-to-eleventy/<figure>
<img src="https://css-irl.info/from-gatsby-to-eleventy-01.jpg" alt="Screenshot of Cassie Evans’ personal site" />
<figcaption><em>Fig 1</em> Cassie Evans’ personal site is resplendent with playful touches</figcaption>
</figure>
<p>There have been <em>so many</em> great personal site redesigns recently. <a href="https://www.cassie.codes/">Cassie’s</a>, <a href="https://www.jason.af/">Jason’s</a> and <a href="https://joshwcomeau.com/">Josh’s</a> are among my favourites, but there are plenty more flying under the radar. What characterises many of them is a certain playfulness, a resurgence of whimsy and delight. You can feel the love and care that has gone into them, the fact that their creators have carved out their little corner of the web to call home. Perhaps the pandemic situation, coupled with the popularity of Animal Crossing has something to do with this?</p>
<p><a href="https://sarahdrasnerdesign.com/">Sarah Drasner</a> has written recently about <a href="https://css-tricks.com/in-defense-of-a-fussy-website/">the case for “fussy” websites</a>. <a href="https://mxb.dev/">Max Bock</a> has put together a collection of come of these exceptional creations under the guise of <a href="https://whimsical.club/">The Whimsical Web</a> – websites that “spark joy” (complete with the most joyful of fonts, Lobster!). It feel like the perfect time to get creative on the web, and bring your personality to your personal site.</p>
<figure>
<img src="https://css-irl.info/from-gatsby-to-eleventy-02.jpg" alt="Screenshot of The Whimsical Web" />
<figcaption><em>Fig 2</em> The Whimsical Web – a showcase of whimsical websites</figcaption>
</figure>
<p>All of this has inspired me to do a little redesign of my own. My personal site, which I have previously <a href="https://css-irl.info/building-a-dependency-free-site/">written about</a>, is a very simple HTML and CSS landing page. There’s no build process, and no JS. While it serves its purpose perfectly well, it doesn’t tell the full story of who I am. I’d like my personal site to have more personality, and become a bit of a playground.</p>
<h2>Eleventy vs. Gatsby</h2>
<p>I hope to work on my personal site over the coming weeks. One of the priorities is to use a static site generator, to make it easy to keep content up-to-date. If you’re unfamiliar with static site generators (SSGs), <a href="https://www.netlify.com/blog/2020/04/14/what-is-a-static-site-generator-and-3-ways-to-find-the-best-one/">this Netlify post</a> explains what they are.</p>
<p>This blog uses <a href="https://www.gatsbyjs.org/">Gatsby</a>, and I briefly considered using it for my personal site too.</p>
<h3>Gatsby</h3>
<p>Gatsby has a lot to recommend it:</p>
<ul>
<li>Most notably, the documentation is fantastic, including well-written tutorials, making it relatively straightforward to get up-and-running with a simple site very quickly.</li>
<li>There is a large community around it, which makes it easier to find solutions to common problems.</li>
<li>Templating in JSX, with all the power of React at your fingertips, makes for a great developer experience.</li>
<li>Gatsby handles bundling and code splitting out of the box, no configuration required, and there are plenty of plugins to handle things like image optimisation.</li>
<li>Sites built with Gatsby feel <a href="https://www.freecodecamp.org/news/how-gatsby-is-so-blazing-fast-c99a6f2d405e/">lightning-fast</a> due to the use of React and built-in optimisations.</li>
</ul>
<p>On the other hand, it has some drawbacks:</p>
<ul>
<li>Everything requires a plugin, and I mean <em>everything</em>. You need a plugin for that. There’s a large plugin-authoring community, but it feels a step removed from the raw materials of the web. Getting your hands dirty in the HTML isn’t really an option, and I kind of miss that.</li>
<li>All of Gatsby’s bells and whistles can feel like overkill for a very simple site.</li>
<li>Even a site that requires no JS, or is completely server-side rendered, ships a bunch of client-side JS in order to feel “lightning fast”. There is an argument for only sending JS to the client when you actually need it, and for a simple static site it might be unnecessary.</li>
</ul>
<h3>Eleventy</h3>
<p><a href="https://www.11ty.dev/">Eleventy</a> is another static site generator that has been getting a lot of attention recently. While it doesn’t have as large a user base as Gatsby, (nor the VC funding to go with it), it does have a growing community, which is helpful for getting started. Just by looking at the growing list of sponsors, you can see it’s built by and for people who care about the web.</p>
<p>With Eleventy you can choose from a range of templating languages. Nunjucks is my preference, but you can also use Liquid, Handlebars or others. I like being able to write real, organic HTML, while JSX feels one step removed. (Not that I’m saying JSX is bad, but writing HTML always feels a bit like coming “home”!)</p>
<p>I’ve been eyeing Eleventy for a while (and dabbled a little bit), and rebuilding my personal site gives me the perfect excuse to take it for a full test drive.</p>
<p>Eleventy is much more of a “roll-your-own” static site generator than Gatsby, and any add-ons need to be configured. It doesn’t replace your build pipeline. I love using <a href="https://parceljs.org/">Parcel</a> for working on side projects, so I thought I’d try it out in combination with Eleventy.</p>
<h3>Eleventy-Parcel: A starter project</h3>
<figure>
<img src="https://css-irl.info/from-gatsby-to-eleventy-03.jpg" alt="Screenshot of Eleventy-Parcel, a starter project" />
</figure>
<p><a href="https://eleventy-parcel.netlify.app/">Eleventy-Parcel</a> is the resulting starter project, which uses Parcel to compile assets. It’s a fairly minimal starter with a couple of simple templates and base styles. I’ll be using it as the base for my personal site redesign.</p>
<p>In the project, Parcel compiles Sass to CSS and handles module bundling and image optimisation, while Browsersync reloads the page whenever your template files or assets change. While it’s designed around my own preferences, if you take it for a spin and it works for you, let me know! I’d love to see what other people build with it.</p>
<h3>Learning Eleventy</h3>
<p>One area where I would say Eleventy is a little lacking is the documentation. It feels slightly harder to navigate than Gatsby’s docs, in that you kind of need to have an idea of what you’re looking for. The <a href="https://www.11ty.dev/docs/getting-started/">Getting Started</a> tutorial assumes a certain level of knowledge, and in that regard it’s not quite as beginner-friendly for someone who might not already be familiar with static site generators.</p>
<p>However, there are a plenty of tutorials around to help you get started:</p>
<ul>
<li><a href="https://www.filamentgroup.com/lab/build-a-blog/">Build your own Blog from Scratch using Eleventy</a> by the Filament Group</li>
<li><a href="https://piccalil.li/course/learn-eleventy-from-scratch/">Learn Eleventy From Scratch</a> (paid course) – a deep dive that goes beyond Eleventy alone, from Andy Bell</li>
<li><a href="https://tatianamac.com/posts/beginner-eleventy-tutorial-parti/">Beginner's Guide to Eleventy</a> by Tatiana Mac. This is great because it actually explains what a static site generator is, and the pros and cons.</li>
</ul>
<h3>Starter projects</h3>
<p>If you don’t fancy wading through documentation and just want to get up-and-running quickly, there are a bunch of starter projects (other than my own) that have been lovingly crafted with this in mind. The website has a <a href="https://www.11ty.dev/docs/starter/">long list</a>, and here are a couple of my recommendations:</p>
<ul>
<li><a href="https://hylia.website/">Hylia</a> by Andy Bell – a simple blog starter, utilising Netlify CMS for content</li>
<li><a href="https://supermaya-demo.netlify.app/">Supermaya</a> by Mike Riethmuller – helps you add rich features, without a complicated build process</li>
</ul>
<h2>Conclusion</h2>
<p>I’m no expert at Eleventy, but if you fancy trying it out then you could do worse than check out some of the resources in this article. Using an SSG is a great choice for developing a personal site, and I hope that in the coming years we’ll see more and more developers leverage Eleventy, Gatsby, and other SSGs to carve out a personal creative niche on the web. Long may the whimsical website revolution continue!</p>
Irregular-shaped Links with Subgrid2020-06-24T00:00:00Zhttps://css-irl.info/irregular-shaped-links-with-subgrid/<p>Card-based UIs are commonly-used web design patterns, and it’s not unusual to build a UI that requires a hover (or focus) effect to be applied to an entire card. There are a few strategies for implementing this effect using CSS, and <a href="https://css-tricks.com/block-links-are-a-pain-and-maybe-just-a-bad-idea/">this CSS Tricks article</a> covers some of them. (None of them is perfect, and they all have their pitfalls!)</p>
<p>But what if our link hover effect needs to affect a number of child items, and they don’t all sit neatly inside a rectangle, like a regular card?</p>
<p>With CSS Grid, we can lay items out on a horizontal <em>and</em> a vertical axis, in order to build visually interesting layouts, like this one:</p>
<figure>
<img src="https://css-irl.info/irregular-shaped-links-with-subgrid-01b.jpg" alt="A web page with two block links consisting of overlapping images and text" />
</figure>
<p>By turning on the the Grid inspector in Firefox’s dev tools, we can see how the items of one of those components are laid out on the grid:</p>
<figure>
<img src="https://css-irl.info/irregular-shaped-links-with-subgrid-04.jpg" alt="Component layout with the numbered grid lines shown" />
</figure>
<p>If, in our UI design, those grid items need to act as links, then it would make sense for a hover effect would be applied to <em>all</em> the grid items whenever <em>one</em> of them is hovered.</p>
<figure>
<img src="https://css-irl.info/irregular-shaped-links-with-subgrid-05.jpg" alt="Hover effect applied to both grid items" />
<figcaption>Hovering on one item triggers the hover effect on both</figcaption>
</figure>
<p>That’s no problem if we hover over the first item: we can use the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/General_sibling_combinator">general sibling combinator</a> to apply the hover effect to the subsequent items.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.grid__img:hover ~ .grid__caption,
.grid__img:focus ~ .grid__caption</span> <span class="token punctuation">{</span>
<span class="token comment">/* Hover and focus styles */</span>
<span class="token punctuation">}</span></code></pre>
<p>But, unfortunately, the general sibling combinator only affects successive siblings, not those preceding. If we hover over the second item, the hover effect is not applied to the first.</p>
<h2>Absolute positioning</h2>
<p>A better option might be to use an absolute-positioned link to cover the entire area of the component. Once again, we can apply the hover effect using the general sibling combinator, but this time we’re ensuring that <em>all</em> the grid items will follow the anchor link and therefore have the hover effect applied:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>grid<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>grid__link<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>grid__img<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/...<span class="token punctuation">"</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>grid__card<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span><span class="token punctuation">></span></span>Eu scelerisque felis<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris ni ut
aliquip ex ea commodo consequat.
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>Read more→<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<pre class="language-css"><code class="language-css"><span class="token selector">.grid</span> <span class="token punctuation">{</span>
<span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token property">grid-template</span><span class="token punctuation">:</span> <span class="token function">repeat</span><span class="token punctuation">(</span>3<span class="token punctuation">,</span> 1fr<span class="token punctuation">)</span> / <span class="token function">repeat</span><span class="token punctuation">(</span>3<span class="token punctuation">,</span> 1fr<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.grid__link</span> <span class="token punctuation">{</span>
<span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token property">grid-template</span><span class="token punctuation">:</span> <span class="token function">repeat</span><span class="token punctuation">(</span>3<span class="token punctuation">,</span> 1fr<span class="token punctuation">)</span> / <span class="token function">repeat</span><span class="token punctuation">(</span>3<span class="token punctuation">,</span> 1fr<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h2>Positioning with Grid</h2>
<p>With grid we don’t actually need absolute positioning here. Instead, we can position it as a grid item that spans the full column and row axes. We’ll need to use <code>z-index</code> to ensure it’s always on top:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.grid__link</span> <span class="token punctuation">{</span>
<span class="token property">grid-area</span><span class="token punctuation">:</span> 1 / 1 / -1 / -1<span class="token punctuation">;</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token property">grid-template</span><span class="token punctuation">:</span> <span class="token function">repeat</span><span class="token punctuation">(</span>3<span class="token punctuation">,</span> 1fr<span class="token punctuation">)</span> / <span class="token function">repeat</span><span class="token punctuation">(</span>3<span class="token punctuation">,</span> 1fr<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">z-index</span><span class="token punctuation">:</span> 1<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<aside><p>Using <a href="https://css-irl.info/negative-grid-lines">negative grid lines</a> is a handy way of positioning an item from the first to the last grid line – providing we’re using explicit tracks, not implicit ones.</p></aside>
<h3>Accessibility</h3>
<p>This is arguably a better option for accessibility, as it means that instead of having three different links all going to the same URL, we can just use one. But we need to ensure that the link will be announced correctly to assistive technologies.</p>
<p>By using <code>aria-labelledby</code> we can give our link an accessible label that corresponds to the component heading. We could also use <code>aria-hidden</code> to ensure that the heading isn’t announced a second time.</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>grid<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>a</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/<span class="token punctuation">"</span></span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>link<span class="token punctuation">"</span></span> <span class="token attr-name">aria-labelledby</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>title<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>a</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>grid__img<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>/...<span class="token punctuation">"</span></span> <span class="token attr-name">alt</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>grid__card<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span> <span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>title<span class="token punctuation">"</span></span> <span class="token attr-name">aria-hidden</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Eu scelerisque felis<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut
aliquip ex ea commodo consequat.
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>Read more→<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>This works correctly when tested using VoiceOver in Safari.</p>
<h2>But...it doesn’t solve the problem</h2>
<p>The problem here is that wherever we hover within the bounding box of our anchor link, the hover effect will be applied. But visually, if you’re hovering over whitespace, you probably <em>don’t</em> want the component to behave as if it’s being hovered. It just doesn’t make a lot of sense.</p>
<figure>
<img src="https://css-irl.info/irregular-shaped-links-with-subgrid-02a.jpg" alt="Illustrating the hover area of the link" />
<figcaption>When the user’s pointer is anywhere over the purple area, the link will be hovered</figcaption>
</figure>
<p>What we actually want is for our link to behave as if it’s shaped like this:</p>
<figure>
<img src="https://css-irl.info/irregular-shaped-links-with-subgrid-03a.jpg" alt="Illustrating the desired hover area of the link" />
<figcaption>For the user, it would be better if only the purple area shown here activated the link’s hover effect</figcaption>
</figure>
<h2>Nested grids to the rescue</h2>
<p>Now we’ll see another reason why CSS Grid is the ideal choice for laying out this component. By making the absolute-positioned anchor link a grid container that matches the component grid, we can overlay the grid items with pseudo-elements. If we add <code>pointer-events: none</code> to the anchor link itself, and <code>pointer-events: auto</code> to the pseudo-elements, the hover effect will only be applied when those areas are hovered.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.grid__link</span> <span class="token punctuation">{</span>
<span class="token property">pointer-events</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.grid__link::before,
.grid__link::after</span> <span class="token punctuation">{</span>
<span class="token property">pointer-events</span><span class="token punctuation">:</span> auto<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h3>An imperfect solution</h3>
<p>This technique works well enough, as long as we can be more-or-less sure of the size of the resolved grid items. If we’re relying on our grid tracks to be intrinsically sized (based on the length or dimensions of the content), then the absolute-positioned pseudo-elements may not align correctly with our original grid items.</p>
<p>In this example, the first grid item contains a long paragraph of content, which makes the auto-sized grid row much taller:</p>
<p>Unfortunately, the items in the second grid (the anchor link) aren’t aware of the original grid children, so they don’t respond accordingly.</p>
<h2>A better solution with Subgrid</h2>
<p>This is where <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout/Subgrid">subgrid</a> comes into play to solve our problem. Subgrid is part of the <a href="https://www.w3.org/TR/css-grid-2/">CSS Grid Level 2</a> specification. It allows us to create a grid container inside a parent grid that inherits the parent grid on the column or row axis (or both).</p>
<p>We can, in turn, make that anchor link a grid as before, but this time we can use <code>grid-template</code> (or the longhand <code>grid-template-rows</code>/<code>grid-template-columns</code>) to instruct it to use a subgrid.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.link</span> <span class="token punctuation">{</span>
<span class="token property">grid-template</span><span class="token punctuation">:</span> subgrid / subgrid<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Now, positioning the pseudo-elements on the grid as before will ensure they align correctly with the original grid items.</p>
<p>This assumes we’re using the default <code>align-items</code> value of <code>stretch</code>. If we use anything other than that, it’s likely we’ll still run into some alignment problems.</p>
<h2>Browser support</h2>
<p>Before we get too excited, let’s look at the reality. At the time of writing, subgrid is only supported in Firefox. That’s it. Which is a shame, because it’s extremely useful, and would probably increase CSS Grid adoption. Once it lands in Chrome (and I believe it will, at some point), then it’ll be much more worthwhile to use in production. I hope that writing about and sharing use cases like this might increase the momentum to bring subgrid support to all browsers.</p>
<p>If you want to use the above technique, you can always provide a (albeit less perfect) fallback for non-supporting browsers – which, happily is only one line of code:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.link</span> <span class="token punctuation">{</span>
<span class="token property">grid-template</span><span class="token punctuation">:</span> <span class="token function">repeat</span><span class="token punctuation">(</span>3<span class="token punctuation">,</span> 1fr<span class="token punctuation">)</span> / <span class="token function">repeat</span><span class="token punctuation">(</span>3<span class="token punctuation">,</span> 1fr<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token atrule"><span class="token rule">@supports</span> <span class="token punctuation">(</span><span class="token property">grid-template</span><span class="token punctuation">:</span> subgrid / subgrid<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token property">grid-template</span><span class="token punctuation">:</span> subgrid / subgrid<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>Then, when subgrid finally hits the mainstream, your site will be ready to go!</p>
<p>See the full demo on Codepen:</p>
<iframe height="560" style="width: 100%;" scrolling="no" title="Grid hover effect with subgrid" src="https://codepen.io/michellebarker/embed/JjGNdNY?height=560&theme-id=dark&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/JjGNdNY">Grid hover effect with subgrid</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<h2>Resources</h2>
<p>If you’re interested in learning more about subgrid, <a href="https://rachelandrew.co.uk/">Rachel Andrew</a> has got your covered with this useful guide:</p>
<p><a href="https://www.smashingmagazine.com/2018/07/css-grid-2/">CSS Grid Level 2: Here Comes Subgrid</a></p>
Mentoring2020-06-11T00:00:00Zhttps://css-irl.info/mentoring/<figure>
<img src="https://css-irl.info/mentoring-01.svg" alt="A simple illustration of two speech bubbles" />
</figure>
<p>This blog has been a little quiet over the past couple of weeks. With everything going on in the world right now, it doesn't feel like the right time to be sharing CSS tips, without acknowledging the real struggles of the people out protesting on the streets following the killing of George Floyd. Black people are literally fighting for the right to exist in a racist world.</p>
<p>There’s no escaping the fact that white people are overwhelmingly over-represented in tech. At the company I work for, we’re starting to have conversations about how we can improve diversity among the team. I’ve donated to <a href="https://secure.actblue.com/donate/ms_blm_homepage_2019">Black Lives Matter</a> and the <a href="https://www.communityjusticeexchange.org/nbfn-directory">National Bail Fund Network</a>, and I’d encourage you to do the same if you have the means. Netlify are currently running a <a href="https://www.netlify.com/donation-matching">donation matching program</a>, where they’ll match donations of up to $25,000 to a list of organisations.</p>
<p>But it’s only the beginning. I want to do more.</p>
<p>I want to help people from under-represented minorities succeed in tech, and I’ve been thinking a lot about how I can contribute. I’d like to offer my services as a mentor. I’m able to lend a few hours of my time each month for one-to-one calls or email exchanges (whichever is most beneficial) with people starting out in the tech world. Here are some things I could help you with:</p>
<ul>
<li>Front end coding help or pair programming</li>
<li>Identifying and working towards your goals</li>
<li>CV/resumé writing</li>
<li>Putting your skills into words for job applications</li>
<li>Interview preparation</li>
<li>Advice on public speaking (preparing a pitch for a conference, or helping structure a talk)</li>
<li>Advice and tips for structuring learning time, and balancing work and family life</li>
<li>Fighting imposter syndrome</li>
</ul>
<p>Are you from an under-represented background and taking your first steps into tech? Or perhaps you already have a junior developer position and are looking to progress your career? If this sounds like you, and you think you’d benefit from any of the above, please drop me an email at <a href="mailto:contact@michellebarker.co.uk">contact@michellebarker.co.uk</a>.</p>
<p>Due to work and family commitments I’ll only have time for two or three mentees at most, but I’ll do what I can. My time is limited to evenings after 8pm (GMT), which might not suit everyone. Depending on demand, if I can’t help I’ll aim to connect you to someone else in my network if at all possible. It won’t fix the big problems of the world, like structural racism, but it’s a small thing I can do right now, and I hope it’ll help someone.</p>
CSS-only Slide-up Caption Hover Effect2020-05-27T00:00:00Zhttps://css-irl.info/css-only-slide-up-caption-hover-effect/<p>Have you ever needed to build a UI where a caption needs to slide over an image on hover, revealing more content?</p>
<figure>
<img src="https://css-irl.info/css-only-slide-up-caption-hover-effect-03.jpg" alt="A grid of 4 items, with content revealed on hover" />
</figure>
<p>This is something I’ve been tasked with implementing in various ways throughout my career. It’s not too difficult to do: Use <code>position: absolute</code> to position the caption over the image, then transform it down, transitioning it back up on hover – not forgetting <code>overflow-y: hidden</code> on the containing element.</p>
<p>Here’s a simple implementation:</p>
<iframe height="471" style="width: 100%;" scrolling="no" title="Simple CSS caption hover" src="https://codepen.io/michellebarker/embed/jObooew?height=471&theme-id=dark&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/jObooew">Simple CSS caption hover</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>The difficulty comes in when the part of the overlay that needs to “peek” out over the image <em>before</em> hover is of indeterminate height. That could easily be the case if, say, you have a grid of team members, some of whom might have long names or job titles that wrap onto multiple lines.</p>
<figure>
<img src="https://css-irl.info/css-only-slide-up-caption-hover-effect-01.jpg" alt="A grid of 4 items, with content revealed on hover" />
</figure>
<p>Transforming the overlay a fixed length could result in some of the text being clipped. You might resort to Javascript to detect the height of the caption titles and set the <code>transform</code> property accordingly. But we can solve this quite satisfactorily with CSS alone.</p>
<p>First we transform the entire overlay down by 100%:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.item</span> <span class="token punctuation">{</span>
<span class="token property">position</span><span class="token punctuation">:</span> relative<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.item__overlay</span> <span class="token punctuation">{</span>
<span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span>
<span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>
<span class="token property">height</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>
<span class="token property">top</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token property">left</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token property">transition</span><span class="token punctuation">:</span> transform 300ms<span class="token punctuation">;</span>
<span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">translate3d</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 100%<span class="token punctuation">,</span> 0<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>We also transform the caption title – the part that will “peek” over the image – up by 100%. Now it peeks over as we want it to initially. Note, we’re also setting the <code>transition</code> property on both the overlay and the caption title with an equal duration, which will help the transition feel smooth:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.item__caption-title</span> <span class="token punctuation">{</span>
<span class="token property">transition</span><span class="token punctuation">:</span> transform 300ms<span class="token punctuation">;</span>
<span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">translate3d</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> -100%<span class="token punctuation">,</span> 0<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>We also need to set our hover and focus transition states. For these cards I’m using an absolute-positioned anchor link, to enable the whole card to be hovered. On hover, both the overlay and the caption title are set back to their original position. I’m also including focus styles, to ensure the caption can be toggled for non-mouse users:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.item__overlay a:hover ~ .item__overlay,
.item__overlay a:focus ~ .item__overlay,
.item__overlay a:hover ~ .item__overlay .item__caption-title,
.item__overlay a:focus ~ .item__overlay .item__caption-title</span> <span class="token punctuation">{</span>
<span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">translate3d</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 0<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>This works pretty well already. But to add a finishing touch and make it feel just a little bit smoother, I like to transition the opacity of the caption body, adding a slight delay so that it only appears once the caption has transitioned most of the way in:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.item__body</span> <span class="token punctuation">{</span>
<span class="token property">opacity</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token property">transition</span><span class="token punctuation">:</span> opacity 500ms 100ms<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.item__overlay a:hover ~ .item__overlay .item__body,
.item__overlay a:focus ~ .item__overlay .item__body</span> <span class="token punctuation">{</span>
<span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">translate3d</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 0<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Here’s the demo with the complete example:</p>
<iframe height="476" style="width: 100%;" scrolling="no" title="Slide up hover effect CSS-only" src="https://codepen.io/michellebarker/embed/pojmdLq?height=476&theme-id=dark&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/pojmdLq">Slide up hover effect CSS-only</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
All the CSS Colours2020-05-19T00:00:00Zhttps://css-irl.info/all-the-css-colours/<p>I recently made <a href="https://codepen.io/michellebarker/full/mdezJwG">a demo</a> where I needed to list <em>all</em> the CSS named colours as options in a <code>select</code> input. I didn’t want to write them all out by hand, but the trouble was I couldn’t find just a list of all 148 colours anywhere as plain text.</p>
<p>What I ended up doing was copying the HTML from <a href="https://css-tricks.com/snippets/css/named-colors-and-hex-equivalents/">this handy CSS Tricks article</a> (which also includes the corresponding hex codes, and is very useful), then replacing the surrounding table markup with the markup I actually needed. I don’t really want to have to do that again – so I’ve created this <a href="https://gist.github.com/mbarker84/e839c545539a043d2454a5a4fef61acb#file-all-css-named-colors-txt-L24">text file on Github</a> in case I (or you) ever need a plain text list of all the CSS colours in the future.</p>
<p>They’re also listed below 🙂</p>
<pre><code>AliceBlue
AntiqueWhite
Aqua
Aquamarine
Azure
Beige
Bisque
Black
BlanchedAlmond
Blue
BlueViolet
Brown
BurlyWood
CadetBlue
Chartreuse
Chocolate
Coral
CornflowerBlue
Cornsilk
Crimson
Cyan
DarkBlue
DarkCyan
DarkGoldenRod
DarkGray
DarkGrey
DarkGreen
DarkKhaki
DarkMagenta
DarkOliveGreen
Darkorange
DarkOrchid
DarkRed
DarkSalmon
DarkSeaGreen
DarkSlateBlue
DarkSlateGray
DarkSlateGrey
DarkTurquoise
DarkViolet
DeepPink
DeepSkyBlue
DimGray
DimGrey
DodgerBlue
FireBrick
FloralWhite
ForestGreen
Fuchsia
Gainsboro
GhostWhite
Gold
GoldenRod
Gray
Grey
Green
GreenYellow
HoneyDew
HotPink
IndianRed
Indigo
Ivory
Khaki
Lavender
LavenderBlush
LawnGreen
LemonChiffon
LightBlue
LightCoral
LightCyan
LightGoldenRodYellow
LightGray
LightGrey
LightGreen
LightPink
LightSalmon
LightSeaGreen
LightSkyBlue
LightSlateGray
LightSlateGrey
LightSteelBlue
LightYellow
Lime
LimeGreen
Linen
Magenta
Maroon
MediumAquaMarine
MediumBlue
MediumOrchid
MediumPurple
MediumSeaGreen
MediumSlateBlue
MediumSpringGreen
MediumTurquoise
MediumVioletRed
MidnightBlue
MintCream
MistyRose
Moccasin
NavajoWhite
Navy
OldLace
Olive
OliveDrab
Orange
OrangeRed
Orchid
PaleGoldenRod
PaleGreen
PaleTurquoise
PaleVioletRed
PapayaWhip
PeachPuff
Peru
Pink
Plum
PowderBlue
Purple
RebeccaPurple
Red
RosyBrown
RoyalBlue
SaddleBrown
Salmon
SandyBrown
SeaGreen
SeaShell
Sienna
Silver
SkyBlue
SlateBlue
SlateGray
SlateGrey
Snow
SpringGreen
SteelBlue
Tan
Teal
Thistle
Tomato
Turquoise
Violet
Wheat
White
WhiteSmoke
Yellow
YellowGreen
</code></pre>
Exciting Things on the Horizon For CSS Layout2020-05-01T00:00:00Zhttps://css-irl.info/exciting-things-on-the-horizon-for-css-layout/<figure>
<img src="https://css-irl.info/exciting-things-on-the-horizon-for-css-layout.svg" alt="a stylized grid illustration" />
</figure>
<p>This past week has brought a few announcements from browser vendors of some exciting things that might have a big impact on CSS layout in the very near future.</p>
<h2>Chrome previews a new Grid inspector</h2>
<p>I’ve long been a fan of Firefox’s Grid inspector. Other browsers just don’t come close when it comes to debugging CSS layout - until now.</p>
<p>This week Chrome teased us with a sneak-preview of a brand new Grid inspector, which might even rival Firefox’s. No more squinting and counting line numbers – Chrome’s inspector shows them to you! Although I’m a Firefox girl at heart, I’ll still be giving it a whirl – and it’ll certainly make debugging in Chrome easier.</p>
<p>From Twitter:</p>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">It's almost here: CSS grid tooling! 🤘<br /><br />Over the last couple of months, we teamed up with <a href="https://twitter.com/EdgeDevTools?ref_src=twsrc%5Etfw">@EdgeDevTools</a> to work on this highly-requested feature. Here is a sneak preview.<br /><br />We have many more ideas on how to make grids easier and more accessible in the future – stay tuned! <a href="https://t.co/b3u4XaEkzv">pic.twitter.com/b3u4XaEkzv</a></p>— Chrome DevTools (@ChromeDevTools) <a href="https://twitter.com/ChromeDevTools/status/1255481965995851782?ref_src=twsrc%5Etfw">April 29, 2020</a></blockquote> <script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<h2>‘gap’ for flexbox supported in Chrome Canary</h2>
<p>Again, Chrome is playing catch-up to Firefox on this one. The <code>gap</code> property has been supported in Grid layout for a while (previously <code>grid-gap</code>), but now it can be used in flexbox too. This makes building <a href="https://every-layout.dev/blog/algorithmic-design/">algorithmic layouts</a> a lot easier - no more hacking around with margins.</p>
<p>This is currently behind a flag in Chrome Canary – you’ll need to enable it.</p>
<p>From <a href="https://twitter.com/argyleink">Adam Argyle</a> on Twitter:</p>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">🎉 flex that gap in the latest release of Chrome Canary 🎉<br /><br />```css<br />display: flex; 🦾<br />gap: 1ch; 🔥<br />```<br /><br />early adopters:<br />help us test it out won't ya!? <br /><br />note:<br />requires web experiments enabled, visit chrome://flags/<a href="https://twitter.com/hashtag/enable?src=hash&ref_src=twsrc%5Etfw">#enable</a>-experimental-web-platform-features in Canary to enable <a href="https://t.co/lG3WusFA4X">pic.twitter.com/lG3WusFA4X</a></p>— Adam Argyle (@argyleink) <a href="https://twitter.com/argyleink/status/1254794309263491072?ref_src=twsrc%5Etfw">April 27, 2020</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<h2>Masonry in Firefox Nightly</h2>
<p>This is a big one, and kind of blindsided me. <a href="https://css-tricks.com/piecing-together-approaches-for-a-css-masonry-layout/">Masonry layouts</a> are something which, as a developer, I’m asked to implement <em>all</em> the time, but which are (currently) only possible using Javascript. There are some ways you can <em>kind of</em> do a similar thing with pure CSS <a href="https://codepen.io/michellebarker/pen/mdyYxGG">(here’s one)</a>, but all of them have their limitations, such as requiring a fixed height on the parent container, or knowing the height of your grid items in advance.</p>
<p>To be able to do this in CSS has always felt like a distant dream. But <a href="https://twitter.com/MiriSuzanne">Miriam Suzanne</a> just announced that masonry grids are being tested in Firefox Nightly!</p>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">"Masonry" Grids are being tested in Firefox Nightly, behind a feature flag. <br /><br />1. go to "about:config"<br />2. toggle "layout.css.grid-template-masonry-value.enabled"<br />3. try it out!<a href="https://t.co/65EW1jOMfi">https://t.co/65EW1jOMfi</a><a href="https://t.co/psOFPVLRk1">https://t.co/psOFPVLRk1</a></p>— [Mia | Miriam] Suzanne? (@MiriSuzanne) <a href="https://twitter.com/MiriSuzanne/status/1255567501359853570?ref_src=twsrc%5Etfw">April 29, 2020</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>It’s still likely to be a long road to widespread browser support – we’re still waiting on other browsers to follow Firefox’s lead and work on <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout/Subgrid">subgrid</a> support, six months down the line. But I really think this has the potential to be a game -changer – almost as much as subgrid. In fact, I would say that I need to build masonry layouts more often than I build layouts that would require subgrid, given that I can usually work around the lack of subgrid support with nested grids and a bit of maths (although it’s far from ideal).</p>
<p>It’s great to see the kind of progress being made on CSS layout. Let’s hope these features become mainstream soon!</p>
<aside>You can contribute to the masonry discussion <a href="https://github.com/w3c/csswg-drafts/issues/4650">on Github</a></aside>
Video: 90 Seconds on CSS Custom Properties2020-04-22T00:00:00Zhttps://css-irl.info/video-90-seconds-on-css-custom-properties/<p>Last year I spoke at <a href="https://futuresync.co.uk/">Future Sync</a> conference, and this year the organisers asked me if I could provide a short recorded introduction to a CSS topic. I put together a 90-second video on CSS custom properties, one of my favourite (relatively) recent CSS features, and thought I’d share it with you here.</p>
<p>There’s also a transcript below.</p>
<p>https://www.youtube.com/embed/3QcXFk6JQf8</p>
<h2>Transcript</h2>
<p>CSS Custom Properties enable you to store values for reuse in your stylesheets.</p>
<p>If you’re familiar with other programming languages, or with CSS preprocessors, it’s likely you’ll have come across variables. CSS Custom Properties are also known as CSS variables, and they have some similarities.</p>
<p>As an example, we can define a colour value variable, which we’ll call <code>primaryColor</code>, on the <code>root</code> element:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">:root</span> <span class="token punctuation">{</span>
<span class="token property">--primaryColor</span><span class="token punctuation">:</span> #f542d7<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>This makes it a global variable, which we can use anywhere in our stylesheet, in place of a property value.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.some-element</span> <span class="token punctuation">{</span>
<span class="token property">background-color</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--primaryColor<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Alternatively, we could define the custom property on a selector, scoping it to the selector and its descendents.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.some-element</span> <span class="token punctuation">{</span>
<span class="token property">--primaryColor</span><span class="token punctuation">:</span> #f542d7<span class="token punctuation">;</span>
<span class="token property">background-color</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--primaryColor<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>When we use a custom property, we can also set a default value. That way, if the custom property is not defined at the point we use it, it will fall back to the default.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.some-element</span> <span class="token punctuation">{</span>
<span class="token property">background-color</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--primaryColor<span class="token punctuation">,</span> #6942f5<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Custom properties can be updated with CSS or Javascript, making them truly dynamic variables.</p>
<p>We can even use them to calculate values of other custom properties, and that lends them to some very cool creative possibilities.</p>
<p>One example is a staggered animation effect.</p>
<p>First, we assign each element a custom property that corresponds to the element’s index:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.some-element:nth-child(2)</span> <span class="token punctuation">{</span>
<span class="token property">--i</span><span class="token punctuation">:</span> 2<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.some-element:nth-child(3)</span> <span class="token punctuation">{</span>
<span class="token property">--i</span><span class="token punctuation">:</span> 3<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Then we use that variable to calculate another custom property, which we’ll call <code>delay</code>:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.some-element</span> <span class="token punctuation">{</span>
<span class="token property">--delay</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--i<span class="token punctuation">,</span> 1<span class="token punctuation">)</span> * 400ms<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Now we can use that custom property in place of the <code>animation-delay</code> value in the shorthand property:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.some-element</span> <span class="token punctuation">{</span>
<span class="token property">--delay</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--i<span class="token punctuation">,</span> 1<span class="token punctuation">)</span> * 400ms<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">animation</span><span class="token punctuation">:</span> fadeIn 1000ms <span class="token function">var</span><span class="token punctuation">(</span>--delay<span class="token punctuation">)</span> forwards<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>That makes our code much more succinct and maintainable, and we don’t have to adjust the values in lots of different places if we want to make a change.</p>
<p>Try it yourself with this Codepen demo:</p>
<iframe height="395" style="width: 100%;" scrolling="no" title="Staggered animation with custom properties" src="https://codepen.io/michellebarker/embed/BaoyZWY?height=395&theme-id=dark&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true" loading="lazy">
See the Pen <a href="https://codepen.io/michellebarker/pen/BaoyZWY">Staggered animation with custom properties</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>Or check out <a href="https://css-irl.info/7-uses-for-css-custom-properties">this article</a> for some other ideas of where they might come in useful.</p>
Tips for Writing for the Web2020-04-21T00:00:00Zhttps://css-irl.info/tips-for-writing-for-the-web/<figure>
<img src="https://css-irl.info/tips-for-writing-for-the-web.svg" alt="Illustrations of a keyboard with pink floating hearts" />
</figure>
<p>Writing for the web is a skill, like any other, that can only be perfected through practice. I’ve been writing on this blog for two years now, and creating content for other sites before that, and I like to think I’m pretty okay at it – though there are always ways to improve! In this article I’ll share a few tidbits of wisdom that I’ve acquired over the years, which might help you if you’re starting to blog, or even making the switch from writing for print.</p>
<p>While these are written with technical content in mind, they could apply to many different types of web content.</p>
<h2>Write while it’s fresh</h2>
<p><a href="https://twitter.com/rachelandrew">Rachel Andrew</a> recently tweeted the following advice, and I couldn’t agree more:</p>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">The best tutorials are very often written by someone who has just learned a thing. It is very easy to forget what it is like to stare at the docs, wondering how on earth you get started, wishing someone would guide you through it.</p>— Rachel Andrew (@rachelandrew) <a href="https://twitter.com/rachelandrew/status/1250371683233431553?ref_src=twsrc%5Etfw">April 15, 2020</a></blockquote> <script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>Writing that article while the experience is fresh in your mind will no doubt make it all the more relevant to the reader. It also means you’re likely to be more excited by the topic, making it more engaging to read.</p>
<p>I try to follow this advice, but don’t always succeed!</p>
<h2>Don’t worry about typos (too much)</h2>
<p>Your readers will forgive your spelling and grammar mistakes, especially if the content is valuable. Don’t let imperfect grammar hold you back from publishing. Try to be more thorough when it comes to checking code examples though – in my experience it can be frustrating trying out a code example from an article and wondering why it doesn’t work.</p>
<p>Also, don’t worry if you don’t feel like you’re a “natural” writer. People are rarely judgemental, and can look past the fact that you haven’t quite found your writing style yet. You can only improve with practice, so don’t let it put you off.</p>
<h2>Get to the point</h2>
<p>Readers want to know as quickly as possible whether an article is going to be relevant to them, and therefore worth investing their time in. Try to sum up what they’ll actually take away from the article in the first paragraph.</p>
<h2>Write in short paragraphs</h2>
<p>One way in which the web differs from print is that it can be harder to hold a reader’s attention. Plenty of people scan an article before deciding if it’s worth their time to read, and a wall of text can look intimidating and put some readers off before they even get started. Short paragraphs make this easier.</p>
<p>Relevant headings for the different sections are handy for this purpose too.</p>
<h2>Explain your terms</h2>
<p>Using a new term for the first time? Explain to your readers what you mean? Using an acronym? Spell it out for the first instance in the article – after that, you’re generally safe to use it throughout.</p>
<h2>Include lots of links</h2>
<p>It’s often useful to include a link when using a term that might be unfamiliar to some readers. For example, I often link to the <a href="https://developer.mozilla.org/en-US/">MDN (Mozilla Developer Network)</a> documentation for a CSS property or specification, especially if it’s something fairly new. Or, if I’m mentioning another developer by name, I’ll include a link to their website or Twitter bio.</p>
<p>Not only that, but anyone who wants to dive deeper into a topic will appreciate links to your sources, as well as demos and sites they might find helpful. A list of resources at the bottom of an article (or relevant section) can be handy.</p>
<p>(Links might also be good for SEO. I don’t have any actual data on this, and this isn’t the reason I include them, but I’m told that’s a thing. Please don’t cram your article with links just for SEO purposes though, as irrelevant links will be more hindrance than help.)</p>
<h2>Write for your readers, not your ego</h2>
<p>This might sound obvious, but it’s sometimes easy to forget when you’re mid-flow. Often I catch myself writing a sentence that sounds eloquent to my ears but, on a second reading, does not sound helpful to someone coming across the information for the first time.</p>
<p>Using a bunch of jargon (especially without explanations), or complex language where a simpler explanation will do the job better, can easily alienate your audience. When I’m writing, I frequently ask myself: “Am I writing this sentence because it’s helpful, or because it makes me sound clever?” If it’s the latter, I delete it.</p>
<h2>Acknowledge your shortcomings</h2>
<p>No-one knows <em>everything</em> about a topic (with a few exceptions!). You don’t have to be an expert at something to write a useful article about it – many of the most useful articles are from a beginner’s perspective. But make it clear to your readers where the limits of your knowledge lie, (and include links to other sources, if possible), so that they can make an informed judgement about your advice or approach.</p>
<h2>Resources and acknowledgements</h2>
<p>This post was part-inspired by an <a href="https://twitter.com/mmatuzo/status/1251857510186856449?s=20">excellent Twitter thread</a> by <a href="https://twitter.com/mmatuzo">Manuel Matuzović</a>, which he then followed up with a <a href="https://www.matuzo.at/blog/blogging/">blog post to summarise</a>.</p>
<p>People much smarter than me have written in much more depth on the subject of technical writing – they also publish highly successful blogs, so definitely take note!</p>
<ul>
<li><a href="https://css-tricks.com/advice-for-technical-writing/">Advice For Technical Writing</a> by Chris Coyier, published on <a href="https://css-tricks.com/">CSS Tricks</a></li>
<li><a href="https://www.smashingmagazine.com/2019/08/pitching-writing-publications/">Pitching Your Writing To Publications</a> by Rachel Andrew, published on <a href="https://www.smashingmagazine.com/">Smashing Magazine</a></li>
<li><a href="https://www.sarasoueidan.com/desk/just-write/">Just Write</a> by Sara Soueidan</li>
</ul>
Different Approaches to Responsive CSS Motion Path2020-04-18T00:00:00Zhttps://css-irl.info/responsive-css-motion-path/<p>When it comes to using <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Motion_Path">CSS Motion Path</a>, it always bugs me that the path itself is doesn’t scale. The path size, as created, is essentially a fixed size, in pixels. It means that responsive design with Motion Path is a little tricky, and requires some workarounds.</p>
<h2>Scaling the path</h2>
<p>Happily, fellow CodePen enthusiast (and awesome animator) <a href="https://twitter.com/jh3yy">Jhey Tompkins</a> has figured out a way to scale a path value, with some imaginative thinking, and charting library <a href="https://d3js.org/">D3</a> - and has <a href="https://css-tricks.com/create-a-responsive-css-motion-path-sure-we-can/">written about it on CSS Tricks</a>. This is a clever way to scale the path if you <em>don’t</em> want the elements on the path to scale themselves. It even allows for scaling the path on either axis, if we don’t want to preserve the aspect ratio. Jhey even put together a demo testing it on one of my own CodePen experiments:</p>
<iframe height="468" style="width: 100%;" scrolling="no" title="Typo-coaster w/ Meanderer" src="https://codepen.io/jh3y/embed/XWbLdVd?height=468&theme-id=dark&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true" loading="lazy">
See the Pen <a href="https://codepen.io/jh3y/pen/XWbLdVd">Typo-coaster w/ Meanderer</a> by Jhey
(<a href="https://codepen.io/jh3y">@jh3y</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<h2>A transform approach</h2>
<p>As Jhey’s demo shows, we’d also need to do some work to scale the text to get a result that’s useful for us. Sometimes it might make more sense to scale not only the path, but the elements animating on it, when the viewport is resized. I tested a different approach: using <a href="https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver">Resize Observer</a> to calculate the transform <code>scale</code> value from a parent container. This outputs a custom property value, which is then used in a CSS transform:</p>
<iframe height="420" style="width: 100%;" scrolling="no" title="Motion path scaling" src="https://codepen.io/michellebarker/embed/zYvOVjo?height=420&theme-id=dark&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true" loading="lazy">
See the Pen <a href="https://codepen.io/michellebarker/pen/zYvOVjo">Motion path scaling</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>This also with other content in the document flow, which I wasn’t sure it would.</p>
<iframe height="631" style="width: 100%;" scrolling="no" title="Motion path scaling" src="https://codepen.io/michellebarker/embed/KKdMMKd?height=631&theme-id=dark&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true" loading="lazy">
See the Pen <a href="https://codepen.io/michellebarker/pen/KKdMMKd">Motion path scaling</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>Both of these approaches would benefit from more experimentation, and could be useful in different cases. I’m looking forward to working with them a bit more, as being able to scale paths would be a big incentive to using Motion Path in production.</p>
Building an Interactive Timetable2020-03-31T00:00:00Zhttps://css-irl.info/building-an-interactive-timetable/<figure>
<img src="https://css-irl.info/building-an-interactive-timetable-01a.jpg" alt="Screenshot of the interactive timetable demo" />
</figure>
<p>This week the UK joined many other parts of the world in imposing a lockdown to battle the coronavirus (AKA Covid-19), and so we find ourselves in a pretty weird situation, to say the least. I hope you’re all keeping safe and taking care of yourselves and your loved ones out there. My heart goes out to those who have been personally affected, and to the incredible NHS workers on the frontline, who are fighting daily battles and risking their own health to keep us safe.</p>
<p>Personally, I find the best way to cope with such a big upheaval is to take control of the small things I can. As parents, my husband and I, like so many others, find ourselves juggling childcare while working from home now that our son’s preschool has shut down. I know he’ll miss his preschool routine, and with our range of activities severely curtailed, it’s time for us to get creative and come up with some ideas for fun games and activities to be played at home. I decided to make a weekday timetable, for us to populate with activities each week, to keep some semblance of routine. And, just for fun (and for extra motivation!), I made it an interactive web timetable.</p>
<p>Although it’s only a fun little project, there were some interesting decisions to make along the way. My favourite type of project is where I get to do some problem-solving and working with constraints. So let’s take a look at some of the practical considerations for building a timetable, and how they influenced the result.</p>
<h2>Design</h2>
<p>I designed my timetable to look a bit like a whiteboard, with post-it notes for the activities that we’re going to populate it with. These are lined up to one side to begin with, then when you click on a button the activities are randomised (to keep things interesting!) and moved into the timetable itself.</p>
<p>As you might expect, the timetable has the days of the week along the top (as column headings), and hours of the day along the left side. To make life easier, we’re splitting the day up by hour, and assuming each activity lasts an hour (or there abouts). There are a number of pre-populated time slots – breakfast and lunch happen at the same time each day, for example.</p>
<h2>Tables and grids</h2>
<p>The first consideration in any web project should be semantic HTML. If you’ve come across this blog before, you might already be aware that I’m a big fan of <a href="https://css-irl.info/building-an-interactive-timetable/">CSS Grid</a>, and it’s often my layout method of choice. But in this case we’re building a <em>table</em>. CSS Grid is not the right choice for building a data table.</p>
<p>HTML <code><table></code> elements, when used correctly, give us the all semantics and layout we need for this purpose out of the box. To build a table as a grid, we would need to flatten the markup, removing the relationship of row and column headings to the content. Needless to say, this would be a disaster for accessibility.</p>
<p>Something you should never do is put <code>display: grid</code> on a <code><table></code>. This renders the table completely inaccessible to screenreaders. On first glance, you might think this is nonsensical anyway: grid items can only be direct children of a grid container, whereas table markup requires nested elements (<code><tbody></code>, <code><tr></code>, <code><td></code>, etc). But Subgrid (part of the <a href="https://drafts.csswg.org/css-grid-2/">CSS Grid Level 2 specification</a>, and currently only supported in Firefox) makes it a little more tempting to do this. Subgrid allows grid items to inherit the parent grid – so you can have multiple levels of descendent elements, which all align to the grid of a common ancestor. It’s fantastically powerful for layout in general, but should not be used on <code><table></code> elements. The <code>display</code> property is one of those properties that alters the HTML semantics. Just don’t do it!</p>
<p>I did, however use Grid for the main layout and activities list, which is perfectly fine.</p>
<aside>If you’d like to learn more about Subgrid, <a href="https://rachelandrew.co.uk/">Rachel Andrew</a> has written and spoken a lot about it. <a href="https://www.smashingmagazine.com/2018/07/css-grid-2">This article</a> is a good place to start.</aside>
<h2>Structuring the table markup</h2>
<p>Now that we’ve settled on using a <code><table></code>, we can add the basic markup before we populate it (we’ll need Javascript for that part). We need to ensure that only the empty table cells will be populated on the click of a button. We <em>could</em> look for only empty cells with JS – something like:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> cells <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token operator">...</span>document<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">'td'</span><span class="token punctuation">)</span><span class="token punctuation">]</span>
cells<span class="token punctuation">.</span><span class="token function">filter</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">el</span><span class="token punctuation">)</span> <span class="token operator">=></span> el<span class="token punctuation">.</span>innerText <span class="token operator">===</span> <span class="token string">''</span><span class="token punctuation">)</span></code></pre>
<p>Once we’ve populated the timetable, however, those cells are no longer empty, so that’s not going to work if we want to sort the activities a second time. Instead, we can use a <a href="https://css-irl.info/building-an-interactive-timetable/">data-* attribute</a> on the cells we want to target. This example shows two table rows, the first containing empty cells (with data-* attributes), the second with pre-populated content. (The <code><span></code> elements are just for styling purposes.)</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</span> <span class="token attr-name">scope</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>row<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>11:00am<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name">data-cell</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name">data-cell</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name">data-cell</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name">data-cell</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span> <span class="token attr-name">data-cell</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>tr</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>th</span> <span class="token attr-name">scope</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>row<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>12:00<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>th</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>Lunch<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>Lunch<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>Lunch<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>Lunch<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>td</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>Lunch<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>td</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>tr</span><span class="token punctuation">></span></span></code></pre>
<p>Now we can select just the cells with need to populate:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> cells <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token operator">...</span>document<span class="token punctuation">.</span><span class="token function">querySelectorAll</span><span class="token punctuation">(</span><span class="token string">'[data-cell]'</span><span class="token punctuation">)</span><span class="token punctuation">]</span></code></pre>
<p>(We could use a class name instead of a data-* attribute for this purpose if we wanted.)</p>
<h2>CSS</h2>
<p>The activities are styled to look like post-it notes. The random colours and slight rotation of each one add to this illusion. We <em>could</em> style these using <code>nth-child</code> or <code>nth-of-type</code> pseudo-selectors, which would work fine to begin with. But when the activities are shuffled into random order on the timetable, this method of styling breaks down. What was originally the first child is now no longer – a post-it that was previously pink might end up green when added to the board.</p>
<p>We want each activity to keep it’s original styling when it’s moved over. The way I chose to tackle this is by using custom properties inline in the HTML for each item.</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>activity<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">--bg</span><span class="token punctuation">:</span> #adff8a<span class="token punctuation">;</span> <span class="token property">--r</span><span class="token punctuation">:</span> 1.25deg<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>Outdoor games<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span></code></pre>
<p>By setting defaults in the CSS, we only need to change the custom property values when we want them to differ from the norm:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.activity</span> <span class="token punctuation">{</span>
<span class="token property">background-color</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--bg<span class="token punctuation">,</span> #fcf3b8<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">transform</span><span class="token punctuation">:</span> <span class="token function">rotate</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--r<span class="token punctuation">,</span> -2deg<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h2>Making it interactive with JS</h2>
<p>For simplicity’s sake, we’re going to assume that our list of possible activities matches exactly the number of available cells. That way, we just need a sprinkle of JS for our desired interactivity.</p>
<p>When we click a button we want the following to happen:</p>
<ol>
<li>Create a new array containing our activities (each one as a HTML element).</li>
<li>Shuffle the array into a random order.</li>
<li>Loop over the empty table cells and add an HTML element from our array into it.</li>
<li>Remove the original list of activities (or hide them from view).</li>
</ol>
<p>I don’t want to reinvent the wheel (also, I’m lazy), so to shuffle the array items I’m using a <code>shuffle</code> function from The Internet:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> <span class="token function-variable function">shuffle</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">array</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">let</span> a <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token operator">...</span>array<span class="token punctuation">]</span>
<span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">let</span> i <span class="token operator">=</span> a<span class="token punctuation">.</span>length <span class="token operator">-</span> <span class="token number">1</span><span class="token punctuation">;</span> i <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">;</span> i<span class="token operator">--</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> j <span class="token operator">=</span> Math<span class="token punctuation">.</span><span class="token function">floor</span><span class="token punctuation">(</span>Math<span class="token punctuation">.</span><span class="token function">random</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">*</span> <span class="token punctuation">(</span>i <span class="token operator">+</span> <span class="token number">1</span><span class="token punctuation">)</span><span class="token punctuation">)</span>
<span class="token punctuation">;</span><span class="token punctuation">[</span>a<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">,</span> a<span class="token punctuation">[</span>j<span class="token punctuation">]</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span>a<span class="token punctuation">[</span>j<span class="token punctuation">]</span><span class="token punctuation">,</span> a<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">]</span>
<span class="token punctuation">}</span>
<span class="token keyword">return</span> a
<span class="token punctuation">}</span></code></pre>
<p>I’m looping over the table cells using the <code>forEach</code> method, mainly because I hate writing <code>for</code> loops. First of all I’m removing any content that is already in the cell (in case we already populated it), then taking an HTML element from our array and inserting it into the cell. We can use the one with the corresponding index, to make sure we don’t add duplicates.</p>
<pre class="language-js"><code class="language-js">cells<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">el<span class="token punctuation">,</span> i</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
el<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> <span class="token string">''</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>i <span class="token operator"><</span> activities<span class="token punctuation">.</span>length<span class="token punctuation">)</span> <span class="token punctuation">{</span>
el<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>activitiesList<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span>innerHTML<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<p>Then I’m hiding the original array by setting the <code>display</code> property:</p>
<pre class="language-js"><code class="language-js">listEl<span class="token punctuation">.</span>style<span class="token punctuation">.</span>display <span class="token operator">=</span> <span class="token string">'none'</span></code></pre>
<p>We can put all that together in a single function:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> <span class="token function-variable function">sortActivities</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token keyword">const</span> activitiesList <span class="token operator">=</span> <span class="token function">shuffle</span><span class="token punctuation">(</span>activities<span class="token punctuation">)</span>
cells<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">el<span class="token punctuation">,</span> i</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
el<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> <span class="token string">''</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>i <span class="token operator"><</span> activities<span class="token punctuation">.</span>length<span class="token punctuation">)</span> <span class="token punctuation">{</span>
el<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> <span class="token template-string"><span class="token template-punctuation string">`</span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span>activitiesList<span class="token punctuation">[</span>i<span class="token punctuation">]</span><span class="token punctuation">.</span>innerHTML<span class="token interpolation-punctuation punctuation">}</span></span><span class="token template-punctuation string">`</span></span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
listEl<span class="token punctuation">.</span>style<span class="token punctuation">.</span>display <span class="token operator">=</span> <span class="token string">'none'</span>
<span class="token punctuation">}</span></code></pre>
<p>There are many different ways we could go about this, and I won’t claim this is absolutely the most efficient methodology, but my goal here was “quick and easy”!</p>
<p>Lastly, I wanted to add another button to clear the timetable and start over. That part is nice and straightforward – just remove the HTML content of each table cell, and restore the visibility of the original activities list:</p>
<pre class="language-js"><code class="language-js"><span class="token keyword">const</span> <span class="token function-variable function">clearTimetable</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
cells<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">el<span class="token punctuation">,</span> i</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
el<span class="token punctuation">.</span>innerHTML <span class="token operator">=</span> <span class="token string">''</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
listEl<span class="token punctuation">.</span>style<span class="token punctuation">.</span>display <span class="token operator">=</span> <span class="token string">'grid'</span>
<span class="token punctuation">}</span></code></pre>
<p>Here’s the complete demo:</p>
<iframe height="547" style="width: 100%;" scrolling="no" title="Kids timetable generator" src="https://codepen.io/michellebarker/embed/KKpBbeo?height=547&theme-id=dark&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/KKpBbeo">Kids timetable generator</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<h2>Wrapping up</h2>
<p>Now we have a simple little interactive timetable. Although I’m planning on creating a real-life version soon (much more enjoyable for little kids, I think!), this one was lots of fun to build and helped me get inspired and motivated. If you’ve read this far, hope you’ve learnt something too.</p>
Positioning Text Along a Path with CSS2020-03-18T00:00:00Zhttps://css-irl.info/positioning-text-along-a-path-with-css/<p>I’ve been playing around with <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Motion_Path">CSS Motion Path</a> a lot over the past couple of months, and having a lot of fun creating demos, some of which you can find in <a href="https://codepen.io/collection/XOOoWv">this Codepen collection</a>. But the “Motion Path” name is a little misleading, as it doesn’t only relate to movement. In fact, although the specification still goes by the name “Motion Path”, the property used to define the path was changed from <code>motion-path</code> to <code>offset-path</code>. It’s easy to see why: an element can be placed anywhere along the path, with <em>or without</em> motion – its position (or <code>offset-distance</code> value) doesn’t have to be animated.</p>
<p>In the following demo (a version of a <a href="https://twitter.com/beesandbombs">@beesandbombs</a> GIF), the circles’ scale and opacity is animated, but their position is in fact stationary, despite the illusion of movement. However, they’re each positioned along a circular path using <code>offset-path</code> and <code>offset-distance</code>.</p>
<iframe height="441" style="width: 100%;" scrolling="no" title="Offset-path animated circles with Houdini and Splitting JS" src="https://codepen.io/michellebarker/embed/dyPeqQx?height=441&theme-id=dark&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/dyPeqQx">Offset-path animated circles with Houdini and Splitting JS</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>(There’s also a <code>conic-gradient</code> mask, animated with <a href="https://developer.mozilla.org/en-US/docs/Web/Houdini">CSS Houdini</a> – you’ll need to view it in Chrome to see the effect.)</p>
<p>This ability to position stationary elements along a path lends itself well to text. I created some demos last year where I used CSS custom properties to position text around a circle, using transforms. This example uses <a href="https://splitting.js.org/">Splitting.js</a> (my favourite JS library!) to set the custom properties. (More on this later.)</p>
<iframe height="440" style="width: 100%;" scrolling="no" title="Text in a circle with CSS variables + Splitting.js and fluid type" src="https://codepen.io/michellebarker/embed/NmgYBY?height=440&theme-id=dark&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/NmgYBY">Text in a circle with CSS variables + Splitting.js and fluid type</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>It works great with dynamic content – you can change the text and the position of each character will be re-calculated so the text fits the circumference perfectly. But there is a downside: the text isn’t selectable, because it uses absolute positioning and transforms.</p>
<h2>Motion Path to the rescue</h2>
<p>If we instead use <code>offset-path</code>, calculating the <code>offset-distance</code> value for each character using custom properties, we can achieve the same effect, with fully selectable text! Nice!</p>
<iframe height="409" style="width: 100%;" scrolling="no" title="Text in a circle with offset-path" src="https://codepen.io/michellebarker/embed/oNXyJxv?height=409&theme-id=dark&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/oNXyJxv">Text in a circle with offset-path</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>Unfortunately, at the time of writing, the only supported <code>offset-path</code> value is a <code>path()</code> function. This doesn’t make it easy to build responsively, as the path doesn’t scale. But there are options, such as using media queries to specify different path values for different breakpoints. Alternatively we could use transforms to scale the whole thing down on smaller screens. But none of these options is ideal. If we could use an SVG path URL (as the specification states), then this would allow much more fine-grained control.</p>
<h3>A word on path creation</h3>
<p>My preferred way to determine the <code>offset-path</code> value is to draw the path in Illustrator and save it as an SVG, before cleaning it up with an optimisation tool and copying the resulting path’s <code>d</code> attribute value. If you simply draw a circle, Illustrator saves this as a <code><circle></code> element rather than a path. One way to prevent this is by first drawing the circle, then using the scissor tool in Illustrator to cut the circle’s path at the desired location. It will then be saved as a <code><path></code> rather than a <code><circle></code> element.</p>
<p>Of course, we’re not limited to circles. We can use any path we choose.</p>
<h2>Yes, we can animate text too</h2>
<p>Animation is where these techniques really shine. We can animate the <code>offset-distance</code> to move the string of text along the path. One of my favourite techniques is to use <code>animation-delay</code> with values calculated from custom properties. Splitting.js assigns each character a custom property corresponding to its index. Passing that into a <code>calc</code> function results in a more organic motion, similar to the effects of easing. You can see the difference in the two animations below. The second animation implements an <code>animation-delay</code> on each character, calculated using custom properties:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.char</span> <span class="token punctuation">{</span>
<span class="token property">--delay</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--char-index<span class="token punctuation">)</span> * 30ms<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<iframe height="409" style="width: 100%;" scrolling="no" title="Comparing offset-path animation with/without delay" src="https://codepen.io/michellebarker/embed/abOKPyg?height=409&theme-id=dark&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/abOKPyg">Comparing offset-path animation with/without delay</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>There’s a lot of fun to be had with this. The following demo uses the techniques described here to create a rollercoaster effect:</p>
<iframe height="451" style="width: 100%;" scrolling="no" title="Typo-coaster" src="https://codepen.io/michellebarker/embed/XWJyydY?height=451&theme-id=dark&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/XWJyydY">Typo-coaster</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>We can get even weirder and add some 3D transforms to our animation:</p>
<iframe height="435" style="width: 100%;" scrolling="no" title="Splitting and motion path" src="https://codepen.io/michellebarker/embed/VwYOvJG?height=435&theme-id=dark&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/VwYOvJG">Splitting and motion path</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>I hope you enjoy playing with Motion Path as much as I have!</p>
In Search of Simplicity2020-02-17T00:00:00Zhttps://css-irl.info/in-search-of-simplicity/<figure>
<img src="https://css-irl.info/in-search-of-simplicity.jpg" alt="A grayscale close-up of a circuitboard" />
</figure>
<p>During the past month or so at <a href="https://www.atomicsmash.co.uk/">Atomic Smash</a> I’ve been working on building our new front end framework (recently christened ‘Snap’) – a repository that includes all the scripts, packages and starter files to enable anyone in the development team to get started on a new project quickly and painlessly. Uppermost in my mind is the elusive goal of simplicity, in a front end development landscape that is anything but.</p>
<p>I’ve <a href="https://css-irl.info/building-a-dependency-free-site">written before</a> about my frustrations with what can sometimes be unnecessary complexity in this industry, and of the refreshing experience of building my small portfolio site in only HTML and CSS. But, as I acknowledged in the article, that approach is almost certainly lacking when it comes to building anything more than a very simple site. We need to embrace development tools, providing they are more help than hindrance.</p>
<h2>What does “simple” mean?</h2>
<p>“Simple” is a relative term, and what is simple for one person may be much more difficult for others. The main goal of the framework is to enable developers on the team to build smarter, faster and more consistently. We want to deliver a great user experience by shipping performant, accessible code. To do this we need to minimise redundancy and emphasise modularity. The design team play a key part in this too – a modular approach focused on building components rather than pages can only be realised if that attitude is permeated throughout an organisation.</p>
<h2>Pragmatic simplicity</h2>
<p>But simplicity of outcome is only one part of the equation. Simplicity of outcome often (necessarily) hides complexity behind the scenes. The framework should be simple for any developer on the team to pick up and start a project with. But I don’t want other developers to feel like they’re picking up a black box. I want team members to feel that they too have ownership over the framework and the projects we build with it. As a team we should aim to build consistently, so that a project can be picked up and worked on by anyone in the team, should the need arise. But the fact is, different people have different areas of expertise, or are at different stages of their development journey – not everyone is comfortable and proficient with the same technology. I could insist that everyone use vanilla Javascript, but some people may be happiest with jQuery, and to try to prevent people using a tool that helps them do their job well is setting them up for a frustrating experience. So the framework should be prescriptive, without being restrictive. It should allow for flexibility and pragmatism.</p>
<h2>Build tools and trade-offs</h2>
<p>After some investigation we chose to use <a href="https://parceljs.org/">Parcel</a> as our build tool. Parcel purports to be a “zero-config” module bundler, and is indeed very simple to get started with – I’ve <a href="https://css-irl.info/a-modern-front-end-workflow-part-2">written previously</a> about how to get a simple project up and running using it. But choosing a build tool comes with trade-offs: Parcel is not as well-established as Gulp and Webpack, for example, so if you run into problems there is less support out there. On the other hand, keeping things relatively simple will (hopefully) result in fewer problems. But is wasn’t long before I found myself digging through more complexity than I’d anticipated, despite my best intentions. Plugins for SVG sprite creation and templating language support, for example, all add to the dependency overhead that needs to be maintained.</p>
<p>For now I’m satisfied that we’ve arrived at <em>just</em> simple enough in order to make using Parcel worthwhile – if we need any more complex configuration it might be time to think about another option.</p>
<h2>Atomic CSS</h2>
<p>I have a love/hate relationship with <a href="https://css-tricks.com/growing-popularity-atomic-css/">atomic CSS</a>, which I’ll spare you the details of. (<a href="https://css-irl.info/a-year-of-utility-classes">Read this article</a> if you want to know my feelings!) Overall I believe the benefits for our team outweigh the pain points, and that applied mindfully, it does speed up UI development. At Atomic Smash we’ve recently opted to use <a href="https://tailwindcss.com/">Tailwind CSS</a>, which I was already pretty familiar with. But one of my frustrations is that it feels like a tool by and for Javascript developers. All the configuration happens in Javascript, and the complexity it adds to the toolchain is largely omitted from the discussion.</p>
<p>For example, if you’re using Tailwind, it’s pretty much essential to use <a href="https://purgecss.com/">PurgeCSS</a> in your build to remove unused CSS, otherwise you ship a big old ~800kb CSS file. But to use Purge effectively you need to do yet more configuration – such as making sure that dynamically-added selectors aren’t purged, for example. The simplicity of the UI-building process is somewhat offset by the added complexity of keeping this machine alive. I spent hours trying to debug Purge, which was removing too many styles, before my colleague <a href="https://twitter.com/david_darke">Dave</a> found the answer down the online equivalent of the back of the sofa (a Stack Overflow answer): a missing <code>!</code> in a code comment (missing also from Purge’s documentation, I might add).</p>
<p>Before anyone tells me I just “don’t understand” the benefits of atomic CSS frameworks – I understand them just fine, I like using Tailwind, and I believe it’s the right tool for the right situation. Just don’t kid yourself that it’s “simpler” than writing plain old CSS/Sass, because it’s not. Value your CSS developers, people.</p>
<h2>Documentation and communication</h2>
<p>I’d take easy-to-use, intuitive tools over well-documented ones any day, but in a world where there aren’t enough of the former, the best we can hope for is the latter. One of the things I love about using <a href="https://www.gatsbyjs.org/">Gatsby</a> is its documentation, which makes it relatively easy to use even without prior knowledge of React, or static site generators.</p>
<p>Documentation is empowering, and taking the time to document our framework properly is a big priority for me, with the goal that any developer picking it up will be able to find out how to get started, learn about best practices and troubleshoot common errors. My hope is that this documentation will continue to grow organically, and others will contribute to it as they become more familiar with using it, as well as contributing to the framework itself.</p>
<p>But documentation isn’t a substitute for direct communication – whether face-to-face if you can do it in person, or over the airwaves if you’re remote. I’m half-convinced that the key to keeping things simple is to talk to each other, and that the value of our communication is greater than the value of our tools. It’s too bad that it’s considered such a “soft” skill.</p>
<aside>My pal <a href="https://twitter.com/hankchizljaw">Andy</a> wrote an <a href="https://hankchizljaw.com/wrote/keeping-it-simple-with-css-that-scales/">excellent talk/article</a> that makes some great (often overlapping) points about CSS and simplicity. Go read it!</aside>
Do We Need CSS4?2020-02-13T00:00:00Zhttps://css-irl.info/do-we-need-css4/<p>Jen Simmons raised an <a href="https://github.com/w3c/csswg-drafts/issues/4770">interesting proposal</a> for the CSS Working Group this week:</p>
<blockquote>
<p>Let’s Define CSS 4
It’s come up quite a few times recently that the world of people who make websites would greatly benefit from the CSS Working Group officially defining ”CSS 4”, and later “CSS 5“, etc.</p>
</blockquote>
<p>This comes hot on the heels of the idea being floated by <a href="https://twitter.com/stubbornella/status/1083768515524349952">Nicole Sullivan</a> and <a href="https://www.quirksmode.org/blog/archives/2020/01/css4_is_here.html">PPK</a> earlier on, and subsequently discussed further by <a href="https://css-tricks.com/css4/">Chris Coyier</a>.</p>
<p>The idea isn’t without its detractors. <a href="https://www.impressivewebs.com/css4-bad-idea/">Louis Lazaris</a> wrote about why he thinks this is a bad idea, and raises some good points:</p>
<blockquote>
<p>[Developers] get excited about the tools I mentioned above – as long as those tools are marketed in a way that’s palatable to them...When you think about it, the marketing pushes that have occurred around features like Flexbox and Grid Layout have been just as valuable as the overall marketing that took place for CSS3 before Flexbox was even a thing.</p>
</blockquote>
<p>But overall, it seems like a lot of us who work in CSS feel that the language (specifically the uptake of new features) would benefit from the kind of marketing push that a major version “bump” would provide, even though it wouldn’t be a strictly technical definition. The big question is, how do we define CSS4? What is included, and what doesn’t make the cut?</p>
<p>For my own part, I do think that packaging a bunch of newer specifications as “CSS4” has the potential to improve awareness and uptake among developers, many of whom are unaware of these despite widespread browser support. CSS is moving at lightning speed, and with so many modules at varying stages of supports, it can be hard to prioritise what to learn without some guidance. Whose job it is to provide this is another question, but the discussion is far from over. I’m interested to see how this discussion progresses.</p>
<h2>Read more</h2>
<ul>
<li><a href="https://github.com/w3c/csswg-drafts/issues/4770">Let’s define CSS4</a> issue proposed by Jen Simmons</li>
<li><a href="https://www.quirksmode.org/blog/archives/2020/01/css4_is_here.html">CSS4 is Here</a> by PPK</li>
<li><a href="https://css-tricks.com/css4/">CSS4</a> by Chris Coyier</li>
<li><a href="https://www.quirksmode.org/blog/archives/2020/02/what_is_css4.html">What is CSS4?</a> by PPK</li>
</ul>
Quick and Easy Dark Mode with CSS Custom Properties2020-02-03T00:00:00Zhttps://css-irl.info/quick-and-easy-dark-mode-with-css-custom-properties/<figure>
<img src="https://css-irl.info/quick-and-easy-dark-mode-with-css-custom-properties.svg" alt="Sun and moon illustration" />
</figure>
<p>Adding “dark mode” support to a website or app is becoming increasingly popular among developers, many of whom favour this setting themselves. Giving users a dark theme option can be beneficial for accessibility, as some people experience headaches or visual difficulties from excessively bright screens, or have trouble reading for long periods on a light background.</p>
<h2>Media queries</h2>
<p>Thanks to the <a href="https://drafts.csswg.org/mediaqueries-5/">level 5 Media Queries specification</a>, implementing dark mode styles in your code for users who prefer it is relatively straightforward. By using the <code>prefers-colour-scheme</code> media query we can specify how our website should look for users who have selected a dark colour scheme preference in their system settings:</p>
<pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">prefers-color-scheme</span><span class="token punctuation">:</span> dark<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token comment">/* Dark mode styles go here! */</span>
<span class="token punctuation">}</span></code></pre>
<p>Alternatively we could go the other way and implement dark mode styles as the default, then override them for users who have specifically selected a light colour scheme, or who have no preference.</p>
<pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">prefers-color-scheme</span><span class="token punctuation">:</span> light<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token comment">/* Light styles */</span>
<span class="token punctuation">}</span>
<span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">prefers-color-scheme</span><span class="token punctuation">:</span> no-preference<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token comment">/* Styles */</span>
<span class="token punctuation">}</span></code></pre>
<p>Obviously you still have to go to the trouble of actually writing your CSS styles!</p>
<p>I decided to implement dark mode on this website recently and, thanks to CSS custom properties, I was able to do this pretty quickly and painlessly.</p>
<h2>Swapping SCSS variables for custom properties</h2>
<p>The first step to implementing dark mode was to refactor my code to use custom properties for any colours, instead of Sass variables. Funnily enough, this was the most difficult part, as it requires renaming the variables - the reasons will soon become clear! Refactoring the primary or brand colour is fairly simple:</p>
<pre class="language-scss"><code class="language-scss"><span class="token property"><span class="token variable">$primary</span></span><span class="token punctuation">:</span> deeppink<span class="token punctuation">;</span></code></pre>
<p>Becomes:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">:root</span> <span class="token punctuation">{</span>
<span class="token property">--primary</span><span class="token punctuation">:</span> deeppink<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>But others required a bit more thought. One such variable is <code>$bg-light</code>, which defined the colour of the light background of the site. To implement dark mode we need to be able to swap the background colour for a darker one, using the same variable name - it no longer makes sense to call it <code>--bgLight</code> when the colour it represents is not light!</p>
<p>I have three main background colours, so I settled on the following:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">:root</span> <span class="token punctuation">{</span>
<span class="token property">--primary</span><span class="token punctuation">:</span> deepPink<span class="token punctuation">;</span>
<span class="token property">--headerBg</span><span class="token punctuation">:</span> #1d1d26<span class="token punctuation">;</span>
<span class="token property">--textColor</span><span class="token punctuation">:</span> #0e0f0f<span class="token punctuation">;</span>
<span class="token property">--textColorInverse</span><span class="token punctuation">:</span> #fcfdff<span class="token punctuation">;</span>
<span class="token property">--bg</span><span class="token punctuation">:</span> #fcfdff<span class="token punctuation">;</span>
<span class="token property">--bgTint</span><span class="token punctuation">:</span> #dfeded<span class="token punctuation">;</span>
<span class="token property">--bgGrey</span><span class="token punctuation">:</span> #e6e8e8<span class="token punctuation">;</span>
<span class="token property">--white</span><span class="token punctuation">:</span> #fcfdff<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>This maximises the reusability of the variables, while still making it obvious what they represent.</p>
<h2>Re-defining the variables for dark mode</h2>
<p>After that, all I needed to do is redefine the variables for the dark colour scheme. Some variables, such as the header background and the <code>--primary</code> colour, don’t need to change at all, and the rest of the colour palette is fairly limited. So I only had a few custom properties that needed to be updated for the dark theme:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">:root</span> <span class="token punctuation">{</span>
<span class="token property">--primary</span><span class="token punctuation">:</span> deepPink<span class="token punctuation">;</span>
<span class="token property">--headerBg</span><span class="token punctuation">:</span> #1d1d26<span class="token punctuation">;</span>
<span class="token property">--textColor</span><span class="token punctuation">:</span> #0e0f0f<span class="token punctuation">;</span>
<span class="token property">--textColorInverse</span><span class="token punctuation">:</span> #fcfdff<span class="token punctuation">;</span>
<span class="token property">--bg</span><span class="token punctuation">:</span> #fcfdff<span class="token punctuation">;</span>
<span class="token property">--bgTint</span><span class="token punctuation">:</span> #dfeded<span class="token punctuation">;</span>
<span class="token property">--bgGrey</span><span class="token punctuation">:</span> #e6e8e8<span class="token punctuation">;</span>
<span class="token property">--white</span><span class="token punctuation">:</span> #fcfdff<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">prefers-color-scheme</span><span class="token punctuation">:</span> dark<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token selector">:root</span> <span class="token punctuation">{</span>
<span class="token property">--bg</span><span class="token punctuation">:</span> #161618<span class="token punctuation">;</span>
<span class="token property">--bgTint</span><span class="token punctuation">:</span> #27272c<span class="token punctuation">;</span>
<span class="token property">--textColor</span><span class="token punctuation">:</span> #dbd7db<span class="token punctuation">;</span>
<span class="token property">--bgGrey</span><span class="token punctuation">:</span> #27272c<span class="token punctuation">;</span>
<span class="token property">--textColorInverse</span><span class="token punctuation">:</span> #0e0f0f<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>That’s literally all the code that’s needed to implement the dark theme! To test the theme I can go into my system settings and change my colour preferences to “dark”. On a Mac they are located under <em>System Preferences > General > Appearance</em>.</p>
<h2>Next steps</h2>
<p>Right now, users of the website will get the dark theme of they’ve set their system preferences to “dark”, but they won’t be able to change it – at least without going back into their system settings, which is highly impractical. That isn’t ideal, as some users might prefer dark mode overall, but prefer the light colour scheme of a particular website. Clearly giving users a choice is preferable.</p>
<p><a href="https://hankchizljaw.com/wrote/create-a-user-controlled-dark-or-light-mode/">Andy Bell</a> wrote a great article on how to add a toggle that allows the user to select their preferred colour scheme, using localStorage to store their preference. It’s something that I’d like to implement pretty soon.</p>
Optimising SVGs for the Web2020-01-26T00:00:00Zhttps://css-irl.info/optimising-svgs-for-the-web/<p>Optimising <a href="https://developer.mozilla.org/en-US/docs/Web/SVG">SVGs</a> (scalable vector graphics) for web projects has the dual benefits of reducing the file size <em>and</em> making them easier to work with. But plenty of times I’ve opened up a web project and found that SVG assets could be made significantly smaller with some straightforward optimisations. In this article I’ll share my process for optimising SVG assets, which may help you if you’re a designer or developer unfamiliar with working with SVG on the web.</p>
<p>I’ll assume that you’re already sold on the benefits of SVG over, say icon fonts or PNGs – but in case you’re not, <a href="https://www.lambdatest.com/blog/its-2019-lets-end-the-debate-on-icon-fonts-vs-svg-icons/">here’s a handy article</a> to persuade you.</p>
<p>Many icon libraries supply SVG assets that are already well-optimised. But if you’re creating your own graphics, or they are supplied by another designer, you might want to run them through a few optimisation steps. I mainly use Adobe Illustrator for creating and editing my SVGs. Here’s a fairly simple icon created in Illustrator (<em>Fig 01</em>):</p>
<figure>
<img src="https://css-irl.info/optimising-svgs-for-the-web_01.png" alt="Black and white CSS IRL logo as SVG" />
<figcaption><em>Fig 01</em></figcaption>
</figure>
<p>We can save this by going to <em>File > Save as</em> and selecting the “SVG” option. However, if we take a look at the code of our saved SVG, we’ll see that it is quite bloated. The code contains a lot of unnecessary data – groups that could be collapsed, paths that could be merged, metadata created by the program itself, and more:</p>
<pre class="language-xml"><code class="language-xml"><span class="token prolog"><?xml version="1.0" encoding="utf-8"?></span>
<span class="token comment"><!-- Generator: Adobe Illustrator 22.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span>
<span class="token attr-name">version</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>1.1<span class="token punctuation">"</span></span>
<span class="token attr-name">id</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Layer_1<span class="token punctuation">"</span></span>
<span class="token attr-name">xmlns</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://www.w3.org/2000/svg<span class="token punctuation">"</span></span>
<span class="token attr-name"><span class="token namespace">xmlns:</span>xlink</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://www.w3.org/1999/xlink<span class="token punctuation">"</span></span>
<span class="token attr-name">x</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0px<span class="token punctuation">"</span></span>
<span class="token attr-name">y</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0px<span class="token punctuation">"</span></span>
<span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 800 800<span class="token punctuation">"</span></span>
<span class="token attr-name">enable-background</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>new 0 0 800 800<span class="token punctuation">"</span></span>
<span class="token attr-name"><span class="token namespace">xml:</span>space</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>preserve<span class="token punctuation">"</span></span>
<span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>g</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>path</span> <span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>M90,675.6L424,97.1l48.5,28l-334,578.5L90,675.6z<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>path</span>
<span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>M547.6,279.3h70.2l-10.1,88.2l80.7-37l21.8,66.7l-87.3,17.6l59.9,65.8l-56.4,40.7l-43.8-76.8l-43.8,76.8l-56.4-40.7
l59.9-65.8L455,397.2l21.8-66.7l80.7,37L547.6,279.3L547.6,279.3z<span class="token punctuation">"</span></span>
<span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>g</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></code></pre>
<p>Every graphics program will have its own way of saving SVGs, but regardless of which one you use, they are still likely to contain a lot of extra data if left unoptimised. Let’s look at some ways to remove this and serve the smallest possible assets for our purpose.</p>
<h2>Running a package</h2>
<p><a href="https://github.com/svg/svgo">SVGO</a> is an NPM package that runs an optimisation process on your SVG assets at build time, and it’s absolutely a good idea to make something like that part of your workflow. But a visual tool often does a better job of removing extra paths and groups, and allows us to adjust (and preview) our optimisation settings depending on the result we want.</p>
<h2>A quick win with SVGOMG</h2>
<p>One way to quickly remove a lot of this extraneous data is to run it through <a href="https://twitter.com/jaffathecake">Jake Archibald</a>’s tool, <a href="https://jakearchibald.github.io/svgomg/">SVGOMG</a> (<em>Fig 02</em>). You can either upload the SVG file or paste in the code directly and, depending on the options selected, your SVG will be greatly slimmed-down, without adverse visual impact. You might need to play around with the different options to get your desired result, especially if your SVG is quite complex, but I generally find that for simple icons I can check most of the options without adversly affecting the result.</p>
<figure>
<img src="https://css-irl.info/optimising-svgs-for-the-web_02.jpg" alt="" />
<figcaption><em>Fig 02</em>SVG icon in the <em>SVGOMG</em> interface</figcaption>
</figure>
<p>After running it through SVGOMG, the code looks like this:</p>
<pre class="language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">xmlns</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://www.w3.org/2000/svg<span class="token punctuation">"</span></span> <span class="token attr-name">viewBox</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>0 0 620.2 606.5<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>defs</span><span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>path</span> <span class="token attr-name">d</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>M0 578.5L334 0l48.5 28-334 578.5-48.5-28zM457.6 182.2h70.2l-10.1 88.2 80.7-37 21.8 66.7-87.3 17.6 59.9 65.8-56.4 40.7-43.8-76.8-43.8 76.8-56.4-40.7 59.9-65.8-87.3-17.6 21.8-66.7 80.7 37-9.9-88.2z<span class="token punctuation">"</span></span><span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></code></pre>
<p>This is far better than leaving the graphic unoptimised, but it does contain an extraneous <code><defs/></code> element. And if the original SVG contains groups, layers and effects, then there is a limit to how far a tool like SVGOMG will be able to optimise it. Far better if we go back to our graphics program and make some edits <em>before</em> running it through an optimisation tool.</p>
<h2>Editing the SVG</h2>
<p>If you know how to write SVG code, then this might produce the cleanest, leanest result. Check out the MDN docs for a <a href="https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths">guide to drawing SVG paths</a>, and <a href="https://www.youtube.com/watch?v=1CDTw_UpQoQ">this video</a> by <a href="https://twitter.com/heydonworks">Heydon Pickering</a> if you’re interested in learning more about it.</p>
<p>But for the vast majority of us, editing an SVG is only made practical using a visual tool. For this example I’m using Adobe Illustrator, but others like Sketch have similar editing capabilities.</p>
<p>How much code you can strip away by editing your SVG depends on its complexity, and its use case. The tips that follow generally apply to icons and simple graphics. Complex illustrations often cannot be edited to such a degree without affecting the end result – and, in some cases, may be better off as PNGs or JPGs.</p>
<h3>Expand groups</h3>
<p>The first thing I do when optimising an SVG is remove any hidden layers, and expand groups where possible. This removes any <code><g></code> tags grouping paths in the SVG code. You might want to keep certain groups intact if you plan to style or animate them. You can expand a group in Illustrator using the shortcut <kbd>Shift</kbd> + <kbd>CMD</kbd> + <kbd>G</kbd> (<em>Fig 03</em>).</p>
<figure>
<img src="https://css-irl.info/optimising-svgs-for-the-web_03.jpg" alt="" />
<figcaption><em>Fig 03</em> The layers panel in Illustrator, showing group with two objects</figcaption>
</figure>
<h3>Convert to paths</h3>
<p>Next I convert any strokes to filled paths, where possible (<em>Fig 04</em>). In Illustrator we can do this using <em>Object > Expand</em>. There may be some exceptions: if you’re styling or animating <code>stroke-dasharray</code> or <code>stroke-dashoffset</code> you’ll need to leave these intact, and likewise if you want to retain the stroke width when scaling the SVG.</p>
<figure>
<img src="https://css-irl.info/optimising-svgs-for-the-web_04.png" alt="Icons shown before stroke expanded and after" />
<figcaption><em>Fig 04</em> Icon shown before and after stroke expanded</figcaption>
</figure>
<p>You can also use the <em>Expand</em> option in Illustrator to convert areas of the image such as simple patterns into individually-selectable paths. For complex or detailed patterns it may be best to avoid this.</p>
<h3>Convert text to outlines</h3>
<p>It is sometimes a good idea to convert text to outlines if the text is purely decorative, or the content will be communicated in another way, such as with a heading, button text or <code>aria-label</code>. While it is fine to make use of the SVG <code><text></code> element, it doesn’t always make sense to do so, particularly if you need to load another web font in order to display your SVG text. We can convert text to paths in Illustrator by selecting it and choosing <em>Type > Convert to outlines</em>.</p>
<h3>Merge paths</h3>
<p>Now that everything in our SVG is a path, we can merge them to ensure as few paths are drawn as possible. Take this example of a “search” icon: the two intersecting paths can be merged into one, resulting in a single path (<em>Fig 05</em>).</p>
<p>To merge paths in Illustrator we select them and use the <em>merge</em> option in the <em>Pathfinder</em> menu.</p>
<p>The exception here is if we want to style or animate any paths individually – in that case we should avoid merging them.</p>
<figure>
<img src="https://css-irl.info/optimising-svgs-for-the-web_05a.jpg" alt="Search icon shown before and after paths merged" />
<figcaption><em>Fig 05</em> Icon shown before and after paths merged</figcaption>
</figure>
<h3>Delete any extra paths or groups</h3>
<p>Once the paths are merged I like to do one final check over the layers and remove any empty layers or duplicate paths that may have been created in the process.</p>
<h3>Fit to artwork bounds</h3>
<p>When I use the SVG icon in my HTML I don’t want to be left with extra space around it that I can’t get rid of. This would be the case if the SVG <code>viewBox</code> is larger than the contents. In Illustrator, I select <em>Object > Artboards > Fit to artwork bounds</em> to make the <code>viewBox</code> dimensions fit the artwork (<em>Fig 06</em>).</p>
<figure>
<img src="https://css-irl.info/optimising-svgs-for-the-web_06.jpg" alt="Search icon shown before and after “Fit to artwork bounds” is selected" />
<figcaption><em>Fig 06</em> The icon on the left has a larger viewBox than is necessary. After selecting “Fit to artwork bounds”, the icon sits snugly within the viewBox, with no extra space (right). </figcaption>
</figure>
<aside>To understand more about the SVG <code>viewBox</code>, take a look at <a href="https://www.sarasoueidan.com/blog/svg-transformations/">this article</a> by Sara Soueidan.</aside>
<h3>Exporting</h3>
<p>Now we’re ready to save the SVG. In Illustrator we can select <em>File > Save as</em> and select SVG as the format. The next screen will give us some options for the SVG. I usually check “Presentation attributes” for the style options.</p>
<p>Once these steps are complete, the SVG is now ready to run through an optimisation tool. For icons, I can usually get away with checking <em>most</em> of the options in SVGOMG, once I’ve run these manual optimisations. You’ll notice the code is much cleaner and leaner! But even this doesn’t always remove everything it’s possible to remove. In the code below I’m still left with an empty <code><defs></code> element, so it’s worth doing one final manual clean-up and removing that in my code editor. Now the SVG is ready to use!</p>
<h2>Using the SVG</h2>
<p>SVGs can be used in a number of ways on the web, including:</p>
<ul>
<li>In an <code><img></code> tag</li>
<li>In the <code>background-image</code> CSS property</li>
<li>Inline in the HTML</li>
</ul>
<h3>Sprites</h3>
<p>For icons in particular, inline SVGs offer the most in terms of performance and flexibility, and best way to use them generally is to create a <a href="https://www.webdesignerdepot.com/2017/05/how-to-create-and-manage-svg-sprites/">sprite</a>. If you don’t want to do this manually, there are NPM packages available that auto-generate SVG sprites. <a href="https://icomoon.io/">Icomoon</a> is an app that offers a similar service.</p>
<p>Then, when it comes to using them, instead of pasting in the whole SVG, we can reference them with the <code><use></code> element:</p>
<pre class="language-xml"><code class="language-xml"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span> <span class="token attr-name">xmlns</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>http://www.w3.org/2000/svg<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>use</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>#myIcon<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></code></pre>
<p>Because we’re using paths, we can use the following CSS to instruct all SVGs to inherit the current colour, rather than use the <code>fill</code> property – which, for an icon system, will help us write less code: An SVG icon used in a button will simply inherit the button’s text colour.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">svg</span> <span class="token punctuation">{</span>
<span class="token property">fill</span><span class="token punctuation">:</span> currentColor<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>We’d need to remove the <code>fill</code> attribute on the SVG itself during the optimisation process in order to style it in this way with CSS.</p>
<h2>Conclusion</h2>
<p>This sounds like a lot of steps to take, but in fact the whole process takes surprisingly little time, if you know your way around your graphics program of choice. After a few tries it becomes muscle-memory. I like to optimise most my SVG icons in a batch at the beginning of the project, to make using them in situe much easier. It’s worth investing a little time upfront to make your graphics easier to work with down the road.</p>
<h2>Learning more about SVG</h2>
<p><a href="https://twitter.com/SaraSoueidan">Sara Soueidan</a> is a well-known authority on all things SVG-related, and has published many in-depth articles on the subject, which you can find <a href="https://www.sarasoueidan.com/tags/svg/">here</a>. I thoroughly recommend reading them if you’re looking to increase your SVG knowledge.</p>
Fun with CSS Motion Path2020-01-06T00:00:00Zhttps://css-irl.info/fun-with-css-motion-path/<figure>
<img src="https://css-irl.info/fun-with-css-motion-path-01.jpg" alt="screenshot of spiral motion path animation" />
</figure>
<p>Animating an element along a path is something we as developers would normally reach for a big old JS library (like <a href="https://greensock.com/gsap/">GSAP</a>) for. But with the new <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Motion_Path">CSS Motion Path</a> module, we can create fancy path animations using only CSS!</p>
<p>I’ve created a couple of fun little animations that play around with these properties – we’ll walk through some of the techniques involved in this article.</p>
<iframe height="449" style="width: 100%;" scrolling="no" title="CSS Motion Path with SVG (Chrome only)" src="https://codepen.io/michellebarker/embed/povdXRW?height=449&theme-id=default&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/povdXRW">CSS Motion Path with SVG (Chrome only)</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<iframe height="455" style="width: 100%;" scrolling="no" title="CSS Motion Path spirograph" src="https://codepen.io/michellebarker/embed/qBEpmGK?height=455&theme-id=default&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/qBEpmGK">CSS Motion Path spirograph</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<h2>Simple path animation</h2>
<p>To create a path animation we need to use <code>offset-path</code> with a value of the path we want to animate along (the syntax is like an SVG path), and animate the <code>offset-distance</code> property:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.obj</span> <span class="token punctuation">{</span>
<span class="token property">offset-path</span><span class="token punctuation">:</span> <span class="token function">path</span><span class="token punctuation">(</span><span class="token string">'M.4 84.1s127.4 188 267.7 0 247.3 0 247.3 0'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">animation</span><span class="token punctuation">:</span> move 2000ms<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token atrule"><span class="token rule">@keyframes</span> move</span> <span class="token punctuation">{</span>
<span class="token selector">100%</span> <span class="token punctuation">{</span>
<span class="token property">offset-distance</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>Here’s a simple example:</p>
<iframe height="365" style="width: 100%;" scrolling="no" title="Simple offset-path animation" src="https://codepen.io/michellebarker/embed/gOboBxB?height=365&theme-id=default&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/gOboBxB">Simple offset-path animation</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>We can also change the rotational behaviour and position of the animated object using <code>offset-rotate</code> and <code>offset-position</code> respectively, which could allow for some cool effects. In this demo you can see the effect of <code>offset-rotate</code> when compared to the default: the second object does not rotate relative to the path, but remains fixed.</p>
<iframe height="374" style="width: 100%;" scrolling="no" title="Offset-path with offset-rotate" src="https://codepen.io/michellebarker/embed/bGNamvP?height=374&theme-id=default&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/bGNamvP">Offset-path with offset-rotate</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<h2>Motion path with SVG</h2>
<p>I was also interested in being able to show the actual path the elements are moving along. In the demos above I’m doing this by including an SVG with the same path co-ordinates in the HTML and using absolute positioning. The spec allows for a URL to be passed to the <code>path()</code> function (similar to <code>clip-path</code>), which would mean we could simply include the SVG path ID, and avoid duplicating the path in CSS. (Our CSS file becomes very messy when we include a complicated path with many coordinates!) But that doesn’t appear to be supported anywhere yet, so we’ll have to make do with using path coordinates.</p>
<p>This also means we have less control over making the animation responsive, as we can’t scale our SVG and have the path match. If we try and change the SVG width then the path remains at its original size. (I’m fairly sure this is the case, as I can’t get it to behave any other way – if you have a solution, please let me know!)</p>
<h2>“Drawing” the path</h2>
<p>Not only can we move an element along the path, we can make it look like it’s drawing the path too. We can already “draw” SVG paths using the <code>stroke-dashoffset</code> and <code>stroke-dasharray</code> properties in CSS – the trick is setting the <code>stroke-dasharray</code> value to the length of the path, then animating from that offset value to 0:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.path</span> <span class="token punctuation">{</span>
<span class="token property">stroke-dasharray</span><span class="token punctuation">:</span> 520<span class="token punctuation">;</span>
<span class="token property">stroke-dashoffset</span><span class="token punctuation">:</span> 520<span class="token punctuation">;</span>
<span class="token property">animation</span><span class="token punctuation">:</span> draw 1000ms<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token atrule"><span class="token rule">@keyframes</span> draw</span> <span class="token punctuation">{</span>
<span class="token selector">100%</span> <span class="token punctuation">{</span>
<span class="token property">stroke-dashoffset</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>(<a href="https://css-tricks.com/svg-line-animation-works/">This article from CSS Tricks</a> breaks it down in finer detail.)</p>
<p>If we use the “drawing” animation with the same duration and timing function (easing) as the <code>offset-path</code> animation, then these will happen simultaneously, and the path will appear as if it’s being drawn by the animated element.</p>
<p>In the second of the two demos at the beginning of this article, the animation moving the object along the path loops through twice for every cycle of the stroke animation. Using <code>stroke-dashoffset</code> the line is drawn in and then out again (going from a positive to a negative offset value), so it appears to be drawn and then subsequently erased:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.path</span> <span class="token punctuation">{</span>
<span class="token property">stroke-dasharray</span><span class="token punctuation">:</span> 520<span class="token punctuation">;</span>
<span class="token property">stroke-dashoffset</span><span class="token punctuation">:</span> 520<span class="token punctuation">;</span>
<span class="token property">animation</span><span class="token punctuation">:</span> draw 1000ms<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token atrule"><span class="token rule">@keyframes</span> draw</span> <span class="token punctuation">{</span>
<span class="token selector">0%</span> <span class="token punctuation">{</span>
<span class="token property">stroke-dashoffset</span><span class="token punctuation">:</span> 520<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">50%</span> <span class="token punctuation">{</span>
<span class="token property">stroke-dashoffset</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">100%</span> <span class="token punctuation">{</span>
<span class="token property">stroke-dashoffset</span><span class="token punctuation">:</span> -520<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<h2>Smoother animation with <code>box-shadow</code></h2>
<p>There’s one more little trick to these animations: When building the first example, I noticed that as it was quite fast, the animation looked jumpy at some points. To make the animation feel more natural I added a box shadow while the object was moving – this creates a kind of motion blur effect, and feels much smoother:</p>
<pre class="language-css"><code class="language-css"><span class="token atrule"><span class="token rule">@keyframes</span> move</span> <span class="token punctuation">{</span>
<span class="token selector">10%</span> <span class="token punctuation">{</span>
<span class="token property">opacity</span><span class="token punctuation">:</span> 1<span class="token punctuation">;</span>
<span class="token property">offset-distance</span><span class="token punctuation">:</span> 0%<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">30%</span> <span class="token punctuation">{</span>
<span class="token property">box-shadow</span><span class="token punctuation">:</span> -0.5rem 0 0.3rem salmon<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">70%</span> <span class="token punctuation">{</span>
<span class="token property">box-shadow</span><span class="token punctuation">:</span> -0.5rem 0 0.3rem salmon<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">90%</span> <span class="token punctuation">{</span>
<span class="token property">opacity</span><span class="token punctuation">:</span> 1<span class="token punctuation">;</span>
<span class="token property">offset-distance</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>
<span class="token property">box-shadow</span><span class="token punctuation">:</span> none<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">100%</span> <span class="token punctuation">{</span>
<span class="token property">opacity</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token property">offset-distance</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<h2>Browser support</h2>
<p>At the time of writing, <code>offset-path</code> is only supported in Chrome – although it can be enabled in Firefox with the <code>layout.css.motion-path.enabled</code> flag, and is set to be supported as standard in the next Firefox release.</p>
<aside>
<h4>Update!</h4>
<p>As of Firefox version 72, you no longer need to flip the flag, it’s enabled by default 😄</p>
</aside>
<h2>Resources</h2>
<p><a href="https://twitter.com/dancwilson">Dan Wilson</a> has created useful selection of <a href="https://codepen.io/danwilson/pens/public">Codepen demos</a> that demonstrate the different properties of Motion Path. He’s also just published <a href="http://danielcwilson.com/blog/2020/01/motion-path-quirks/">an article</a> on it. (Thanks <a href="https://twitter.com/cobra_winfrey">Adam Kuhn</a> for pointing me in his direction!)</p>
Imperfect2020-01-03T00:00:00Zhttps://css-irl.info/imperfect/<p>Different people have differing views on writing when it comes to personal blogs. Some people spend a long time perfecting a draft, making sure everything reads well, there are no grammatical errors, and all the code examples are helpful and flawless. Others dash out a draft and publish as quickly as possible, then worry about editing later.</p>
<p>I used to be in the former camp, now I increasingly find myself in the latter. As any parent will no doubt recognise, since having my son in 2016, my spare time is extremely limited. I get home from work, have dinner, play with my son, run him a bath and put him to bed. By the time I’ve done that, plus any other household chores, it’s often 8:30 or 9pm. If I’m not exhausted, then I have around two hours to work on any side projects, play around making demos on <a href="https://codepen.io/">Codepen</a>, or write for my blog. And yet, I feel like it’s the most productive I’ve ever been.</p>
<p>Given the constraint of time, I’ve pushed myself to put things out into the world that are hurried, that have flaws, that are sometimes half-baked ideas, and that have a <em>lot</em> of potential for improvement. I’ve let go of the idea of “perfection” because, for me, it’s impossible – and it was already an unattainable goal to begin with. A bit like parenting, funnily enough.</p>
<p>It’s also why I no longer do much drawing and illustration work – I was never happy with a piece of illustration until it was perfect, while coding (for me, at least) feels like a series of small wins, rather than one big prize.</p>
<p>This has been liberating. There is a huge amount of satisfaction to be gained from deploying <em>something</em> at the end of a long day, instead of waiting and waiting until a project is just right. Sometimes I go back and edit later, sometimes I deem something a low enough priority to let it slide, and that’s okay.</p>
<p>It’s okay to be a perfectionist – I admire people with beautifully-curated portfolios and Codepen profiles, but I can finally recognise that <em>not</em> having that, and channelling your energy elsewhere, is not a failing.</p>
<h2>Redesigning this site</h2>
<p>This brings me onto my goal for 2020, which was redesigning this blog. I wrote in my <a href="https://css-irl.info/2019-in-review/">2019 review</a> that I wanted to redesign and rebuild from the ground up. But, on reflection, that feels like a daunting task, and one that has the potential to slip into that cycle of chasing perfection. On a whim, on 1st January 2020, I decided to see if I could roll out a redesign (with a little refactoring here and there), and tackle the rebuild later (if at all). The first iteration, deployed at 11:55pm on 1st January, was far from perfect: the <a href="https://css-irl.info/about/">About</a> and 404 pages were largely unstyled, I hadn’t updated the favicon or social media images, and there were a thousand little styling improvement that could be made. But it was <em>something</em> – it was usable, and it was (I think) better than what was before.</p>
<p>I still plan to spend time working on a lot of improvements over the next year, but I’ll do it knowing that 2020 is already off to a great start.</p>
2019 in Review2019-12-30T00:00:00Zhttps://css-irl.info/2019-in-review/<figure>
<img src="https://css-irl.info/2019-in-review.svg" alt="2019 text with fireworks" />
</figure>
<p>I’m going to try to keep this 2019 retrospective brief, but like many people, I find it cathartic to look back at the year’s triumphs and disappointments (not dwelling for too long on the latter), and ready for myself for the year ahead.</p>
<p>I prefer to avoid setting myself unrealistic or unattainable goals for the year ahead. But I do like to set myself small, achievable ambitions and milestones, which won’t bring the world crashing down if I pivot from the goal, or if life gets in the way, but will bring a bit of satisfaction to have something to check of the list by this time next year. So, please excuse me while I take a (slightly self-indulgent) look at the year just gone.</p>
<h2>This blog</h2>
<p>I’m constantly blown away by the (relative) success of this blog. Over the past year I’ve received so many nice messages that have boosted my confidence, from people who have found my articles helpful, as well as a few people in the industry I really look up to and admire for their work. It never gets tiring to hear, and constantly reminds me why I write. So, thank you, if you’ve read this blog and learnt something, or shared it with your friends.</p>
<p>I set myself the aim of publishing at least two articles per month, and I’ve stuck to that – allowing myself the occasional dry spell of a few weeks, but always making up for it later. This feels about right, and I have no plans to decrease the output in 2020.</p>
<p>One goal I set myself at the end of last year was to do more illustration for this blog. I’ve done reasonably well at illustrating concepts for articles, but not really as much as I’d like. Hopefully I can improve on that a bit more in 2020.</p>
<p>A couple of (controversial?) decisions I took in 2019 were, firstly to allow ads on the blog, and secondly to add analytics. I was approached by Carbon about advertising on this platform, and although I was initially skeptical, a bit of investigation (including recommendations from well-known industry figures) convinced me that they are a reputable partner. Ads are currently limited to the bottom of articles, so they are as unobtrusive as possible. Rest assured, I’m not making huge amounts of money from this, and I’m not currently planning to expand the advertising any further, so you won’t see intrusive ads creeping in during the coming year.</p>
<p>Adding analytics was a personal decision, and one that I contemplated for a while before taking the plunge. I find it useful and interesting to see what kind of articles people are reading on the site, and seeing a gradual increase in users over time is more rewarding than the instant-hit validation of Twitter. But I still consider this a trial period, and it might be something I remove a little way down the road.</p>
<h3>What’s next for CSS {IRL}?</h3>
<p>Top of the list is I’m considering a move away from Gatsby, the static site generator this blog currently uses. I don’t hate Gatsby, and it has a lot to recommend it – notably the process of getting started was super smooth, with great documentation and tutorials. But as the site has grown, I’ve found it more challenging to add new features, and come up against a few roadblocks.</p>
<p>While I was using React daily, it made sense to continue to use a React-based SSG, but now that I’m no longer using it for my day-to-day work I feel this is a good opportunity to branch out and try something new. I’m considering <a href="https://www.11ty.dev/">11ty</a>, and both <a href="https://hylia.website/">Andy Bell</a> and <a href="https://github.com/MadeByMike/supermaya">Mike Riethmuller</a> have published starter kits, which I expect to learn a lot from, or even use directly. Another possibility is <a href="https://gridsome.org/">Gridsome</a>, which is Vue-based. I’m starting to learn Vue for work, so it seems like it could be a good opportunity to try something new.</p>
<p>I want to completely rebuild the site from the ground up, and that includes a brand new redesign too. A lot of the functionality feels bolted on, rather than being integral to the design from the beginning, so it would benefit from due consideration. I want the redesign to include a better way to search for articles, and find other articles related to the one you’re reading.</p>
<p>This alone is a pretty ambitious goal, and one that I anticipate taking up a fair bit of time. Aside from that, I don’t foresee any major changes from a content standpoint. I find myself naturally writing a little less about CSS Grid these days and a little more about other CSS features specifications that interest (like <a href="https://24ways.org/2019/beautiful-scrolling-experiences-without-libraries/">scroll snap</a> and <a href="https://css-irl.info/7-uses-for-css-custom-properties/">custom properties</a>, but I won’t be leaving Grid behind entirely. As I use it more and more in my daily workflow, I’m sure I’ll always find new things to write about it. I hope next year I’ll find some time to really explore the possibilities that <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout/Subgrid">subgrid</a> brings.</p>
<h2>Speaking</h2>
<p>In 2019 I became a <a href="https://events.mozilla.org/techspeakers">Mozilla Tech Speaker</a>, and spoke at a total of nine events. While that’s not a lot for some people, it’s certainly enough for me! Some personal highlights were <a href="http://2019.upfrontconf.com/">Upfront</a> (a conference I’ve attended several times), <a href="https://2019.stateofthebrowser.com/">State of the Browser</a> (which I was honoured to be invited to speak at for the second year in a row!), and <a href="https://www.londoncss.dev/">London CSS</a>, run by the dynamic duo, <a href="https://twitter.com/ohhelloana">Ana Rogrigues</a> and <a href="https://twitter.com/oliverturner">Oliver Turner</a>. While there are elements of speaking at conferences that I really enjoy, and I’m grateful for the opportunities that speaking has brought me, I find the whole process quite stressful. Make no mistake, preparing a talk takes <em>a lot</em> of work! Between speaking and writing articles, I’ve spread myself pretty thinly this year, and in 2020 I want to give myself sufficient time for creative personal projects. I still plan to do some speaking (I have a couple of engagements lined up already), but I’ll be more selective about which opportunities I say “yes” to.</p>
<h2>Writing</h2>
<p>As well as writing on this blog, this year I’ve written articles for Web Designer magazine, <a href="https://blog.logrocket.com/">LogRocket</a> and <a href="https://24ways.org/">24Ways</a>. The latter is a web developers’ advent calendar I’ve been reading for years, so closing the year by having one of my articles published on it felt like a huge personal achievement. I was blown away by the reception the article got and am honoured to be in such great company!</p>
<p>Being asked to write a cover feature for Web Designer magazine was an exciting new challenge, and gave me the perfect excuse to explore some new CSS specifications I hadn’t previously had the chance to dig into.</p>
<p>I’m open to writing for other publications in 2020, but want to maintain a healthy balance, reserving most of my writing for this blog.</p>
<h2>Work</h2>
<p>After a year of working with React Native at mobile ordering startup <a href="https://ordoo.co.uk/">Ordoo</a>, in November I returned to agency life, as lead front end developer at <a href="https://www.atomicsmash.co.uk/">Atomic Smash</a>. A year of (mostly) writing Javascript has been enormously valuable to me as a developer, but I ultimately wanted to be working on the web again, and especially with CSS. I’m excited to be working with such a great team, and to tackle some exciting challenges in the New Year. Every week provides me with new discoveries, and my work has already furnished me with a growing backlog of writing topics – which I hope to explore on here in the months ahead.</p>
<h2>Personal</h2>
<p>More generally, as we’ve reached the end of a decade, I want to take a moment to look back and feel proud. I can scarcely believe I transitioned from a career in events management less than five years ago, and managed a large part of that major career change while raising my son. My journey into tech was a gradual process (maybe I’ll write about it someday), it hasn’t always been easy, and I’m thankful for the support of my family. For anyone beginning their journey, especially under similar circumstances, please know that it <em>is</em> possible. If you need any resources or guidance, please send me a message and I’ll do the best I can.</p>
<h2>CSS</h2>
<p>This being a CSS blog, I can’t leave without a few thoughts on what excites me about CSS for 2020!</p>
<h3>Subgrid</h3>
<p>Towards the end of 2019, Firefox shipped support for subgrid, part of the <a href="https://www.w3.org/TR/css-grid-2/">CSS Grid Level 2 specification</a>. This has the potential to be a bit of a game changer, and could inspire a whole lot of people to adopt CSS Grid who have otherwise resisted until now...but unfortunately, at the time of writing, no other browsers show signs of implementing it. Until Chrome jumps on this, any real-world use is going to be limited at best. (There is an <a href="https://bugs.chromium.org/p/chromium/issues/detail?id=618969">open issue</a>.) Let’s hope 2020 is the year that other browsers move towards support.</p>
<h3>Custom properties</h3>
<p>I’ve written quite a lot about <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/--*">custom properties</a> on this blog, and I’m finding more uses for them all the time (I collated some <a href="https://css-irl.info/7-uses-for-css-custom-properties/">here</a>). As adoption increases, I think we’ll see more and more creative and useful applications for them.</p>
<h3>Logical properties</h3>
<p>In a nutshell, where we have CSS property names that refer to the left, right, top or bottom of an element (think <code>margin-left</code> or <code>border-top</code>), <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Logical_Properties">logical properties</a> reframe these to refer to the start or end of the <code>block</code> or <code>inline</code> axis. In a horizontal left-to-write language (like English), <code>margin-left</code> becomes <code>margin-inline-start</code>, for example. But in a right-to-left language, this would be the same as using <code>margin-right</code>.</p>
<p>There’s a lot to unpack here, and <a href="https://adrianroselli.com/2019/11/css-logical-properties.html">this article by Adrian Roselli</a> is a good starting point. The implications for designing and developing internationally and inclusively for the web are pretty exciting.</p>
<h3>Variable fonts</h3>
<p>I’ve had a lot of fun experimenting with <a href="https://css-irl.info/variable-font-animation-with-css-and-splitting-js/">variable fonts</a> in the latter half of 2019, and can’t wait to see what new variable fonts become available in 2020. As well as potentially significant performance improvements, variable fonts open up a whole host of creative possibilities. I hope we’ll see designers and developers grasping these as the technology develops and more foundries jump on the bandwagon. For inspiration, look no further than <a href="https://twitter.com/Mandy_Kerr">Mandy Michael</a>’s <em>24 Ways</em> article, <a href="https://24ways.org/2019/interactivity-and-animation-with-variable-fonts/">Interactivity and Animation with Variable Fonts</a>.</p>
<h3>Personalisation</h3>
<p>Perhaps 2020 will be the year the idea of “pixel perfection“ finally dies a death. We already have very little control over how users consume our content – device type and screen size are all unknowns, as are things like whether someone is using a screen reader or keyboard navigation, or has zoomed the web page or adjusted their font size, not to mention the environmental conditions in which someone is using your website (low-light conditions, for example). With the advent of <a href="https://drafts.csswg.org/mediaqueries-5/">Level 5 media queries</a>, we can cater for all sorts of possible scenarios and provide a more tailored experience (such as providing a dark theme by using the <code>prefers-colour-scheme</code> media query), while recognising that our websites are absolutely not going to look the same for everyone using them.</p>
<h3>Flex gap</h3>
<p>Firefox has already shipped support for the <code>gap</code> property in flexbox – a seemingly little thing that could make building flex layout a <em>lot</em> easier! Now let’s hope some other browsers follow suit (looking at you, Chrome).</p>
<p>No doubt there’s much more that I’m forgetting right now. But the future of CSS is looking bright!</p>
7 Uses for CSS Custom Properties2019-12-09T00:00:00Zhttps://css-irl.info/7-uses-for-css-custom-properties/<p><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties">Custom properties</a> (also known as CSS variables) allow us to store property values for re-use in our stylesheets. If you’re relatively new to them, you might wonder when you might use them over and above preprocessor variables (if indeed you use a preprocessor). I’m using custom properties a lot in my workflow these days, and thought I would collate some of the use cases here.</p>
<p>This isn’t an in-depth guide to how custom properties work, so if you need a primer I recommend the following resources:</p>
<h2>Colour functions</h2>
<p>Custom properties don’t <em>just</em> represent entire property values – they can be used to store partial values too. A commonly cited use case is in CSS colour functions. <a href="https://tympanus.net/codrops/css_reference/hsla/">HSLA</a> lends itself particularly well to custom properties, allowing us as developers an unprecedented level of control when it comes to mixing colours.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.some-element</span> <span class="token punctuation">{</span>
<span class="token property">background-color</span><span class="token punctuation">:</span> <span class="token function">hsla</span><span class="token punctuation">(</span>
<span class="token function">var</span><span class="token punctuation">(</span>--h<span class="token punctuation">,</span> 120<span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token function">var</span><span class="token punctuation">(</span>--s<span class="token punctuation">,</span> 50<span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token function">var</span><span class="token punctuation">(</span>--l<span class="token punctuation">,</span> 50<span class="token punctuation">)</span><span class="token punctuation">,</span>
<span class="token function">var</span><span class="token punctuation">(</span>--a<span class="token punctuation">,</span> 1<span class="token punctuation">)</span>
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.some-element.darker</span> <span class="token punctuation">{</span>
<span class="token property">--l</span><span class="token punctuation">:</span> 20<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>We can also do some very cool things like calculate complementary colours. <a href="https://blog.logrocket.com/how-to-create-better-themes-with-css-variables-5a3744105c74/">This article</a> I wrote last year is a much more in-depth guide to colour manipulation with custom properties, and <a href="https://www.sarasoueidan.com/blog/hex-rgb-to-hsl/">Sara Soueidan</a> has a great article on the subject too.</p>
<h2>Shorthand properties</h2>
<p>If you’re using a shorthand property such as <code>animation</code>, and you need to change one value for a different element, then writing out the whole property again can be error-prone and adds an extra burden of maintenance. Using custom properties we can adjust a single value in the shorthand property very easily:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.some-element</span> <span class="token punctuation">{</span>
<span class="token property">animation</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--animationName<span class="token punctuation">,</span> pulse<span class="token punctuation">)</span> <span class="token function">var</span><span class="token punctuation">(</span>--duration<span class="token punctuation">,</span> 2000ms<span class="token punctuation">)</span> ease-in-out
infinite<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.some-element.faster</span> <span class="token punctuation">{</span>
<span class="token property">--duration</span><span class="token punctuation">:</span> 500ms<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.some-element.shaking</span> <span class="token punctuation">{</span>
<span class="token property">--animationName</span><span class="token punctuation">:</span> shake<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h2>Repeated values</h2>
<p>Suppose we have an element that has a consistent value for its top padding, but the same value for all the other sides. Writing the following could be a bit tedious, especially if we want to adjust the padding values:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.some-element</span> <span class="token punctuation">{</span>
<span class="token property">padding</span><span class="token punctuation">:</span> 150px 20px 20px 20px<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">min-width</span><span class="token punctuation">:</span> 50em<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token selector">.some-element</span> <span class="token punctuation">{</span>
<span class="token property">padding</span><span class="token punctuation">:</span> 150px 60px 60px 60px<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>Using custom properties means we have just one place to adjust that padding. Even better, if it’s a standard value that’s used throughout the site then we could declare it in a variable partial, config file or our site’s <a href="https://css-tricks.com/what-are-design-tokens/">design tokens</a>.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">:root</span> <span class="token punctuation">{</span>
<span class="token property">--pad</span><span class="token punctuation">:</span> 20px<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">min-width</span><span class="token punctuation">:</span> 50em<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token selector">:root</span> <span class="token punctuation">{</span>
<span class="token property">--pad</span><span class="token punctuation">:</span> 60px<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token selector">.some-element</span> <span class="token punctuation">{</span>
<span class="token property">padding</span><span class="token punctuation">:</span> 150px <span class="token function">var</span><span class="token punctuation">(</span>--pad<span class="token punctuation">)</span> <span class="token function">var</span><span class="token punctuation">(</span>--pad<span class="token punctuation">)</span> <span class="token function">var</span><span class="token punctuation">(</span>--pad<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h2>Complex calculations</h2>
<p>Custom properties can be really handy for storing calculated values (from the <code>calc()</code> function), which themselves can even be calculated from other custom properties. One example is calculating complementary colours, as mentioned earlier. Another is when you want to calculate the inverse of a property. I wrote an article for CSS Tricks a little while ago on <a href="https://css-tricks.com/reversing-an-easing-curve/">calculating the reverse of an easing curve</a> with custom properties.</p>
<p>I often use custom properties with <code>clip-path</code> if I need to calculate a path relative to another, or relative to known variables. The following code from a recent demo calculates the clip path points for two pseudo elements to give the appearance of an element being bisected.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.element</span> <span class="token punctuation">{</span>
<span class="token property">--top</span><span class="token punctuation">:</span> 20%<span class="token punctuation">;</span>
<span class="token property">--bottom</span><span class="token punctuation">:</span> 80%<span class="token punctuation">;</span>
<span class="token property">--gap</span><span class="token punctuation">:</span> 1rem<span class="token punctuation">;</span>
<span class="token property">--offset</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--gap<span class="token punctuation">)</span> / 2<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.element::before</span> <span class="token punctuation">{</span>
<span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>
<span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--top<span class="token punctuation">)</span> + <span class="token function">var</span><span class="token punctuation">(</span>--offset<span class="token punctuation">)</span><span class="token punctuation">)</span> 0<span class="token punctuation">,</span>
100% 0<span class="token punctuation">,</span>
100% 100%<span class="token punctuation">,</span>
<span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--bottom<span class="token punctuation">)</span> + <span class="token function">var</span><span class="token punctuation">(</span>--offset<span class="token punctuation">)</span><span class="token punctuation">)</span> 100%
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.element::after</span> <span class="token punctuation">{</span>
<span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>
<span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--top<span class="token punctuation">)</span> - <span class="token function">var</span><span class="token punctuation">(</span>--offset<span class="token punctuation">)</span><span class="token punctuation">)</span> 0<span class="token punctuation">,</span>
<span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--bottom<span class="token punctuation">)</span> - <span class="token function">var</span><span class="token punctuation">(</span>--offset<span class="token punctuation">)</span><span class="token punctuation">)</span> 100%<span class="token punctuation">,</span>
0 100%<span class="token punctuation">,</span>
0 0
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<iframe height="397" style="width: 100%;" scrolling="no" title="His Dark Materials TV series logo with CSS" src="https://codepen.io/michellebarker/embed/yLLGVMQ?height=397&theme-id=default&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/yLLGVMQ">His Dark Materials TV series logo with CSS</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<h2>Staggered animations</h2>
<p>If we want to stagger animations for a number of child elements, we can elegantly set the <code>animation-delay</code> on each one by simply defining the custom property as the element’s index:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.element</span> <span class="token punctuation">{</span>
<span class="token property">--delay</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--i<span class="token punctuation">,</span> 0<span class="token punctuation">)</span> * 500ms<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">animation</span><span class="token punctuation">:</span> fadeIn 1000ms <span class="token function">var</span><span class="token punctuation">(</span>--delay<span class="token punctuation">,</span> 0ms<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.element:nth-child(2)</span> <span class="token punctuation">{</span>
<span class="token property">--i</span><span class="token punctuation">:</span> 2<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.element:nth-child(3)</span> <span class="token punctuation">{</span>
<span class="token property">--i</span><span class="token punctuation">:</span> 3<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p class="codepen" data-height="433" data-theme-id="default" data-default-tab="css,result" data-user="michellebarker" data-slug-hash="VwYYpqw" style="height: 433px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Staggered animation with custom properties">
<span>See the Pen <a href="https://codepen.io/michellebarker/pen/VwYYpqw">
Staggered animation with custom properties</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>)
on <a href="https://codepen.io/">CodePen</a>.</span>
</p>
<script async="" src="https://static.codepen.io/assets/embed/ei.js"></script>
<p>Unfortunately we currently have to assign the variable explicitly, which could be a problem if we have an indeterminate number of children. <a href="https://splitting.js.org/">Splitting JS</a> is a great Javascript library that takes care of that by assigning the element’s index as a variable, and is very useful for this kind of staggered animation. But it would be great not to have to use JS!</p>
<p>Adam Argyle has recently submitted <a href="https://github.com/w3c/csswg-drafts/issues/4559">a proposal</a> for two new CSS functions, <code>sibling-count()</code> and <code>sibling-index()</code>, which would be a game-changer, making a whole lot of new things possible with CSS. They’re nowhere close to being adopted by any browsers at this point, but it would be an incredibly powerful addition, so one to keep an eye on.</p>
<h2>Responsive grids</h2>
<p>I’ve written about it <a href="https://css-irl.info/super-powered-layouts/">on this blog before</a>, but custom properties can help make complex Grid layouts easier to manage. Suppose we have an 8-column grid, which we want to change to a 12-column grid at a specific breakpoint:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">:root</span> <span class="token punctuation">{</span>
<span class="token property">--noOfColumns</span><span class="token punctuation">:</span> 8<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token atrule"><span class="token rule">@media</span> <span class="token punctuation">(</span><span class="token property">min-width</span><span class="token punctuation">:</span> 60em<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token selector">:root</span> <span class="token punctuation">{</span>
<span class="token property">--noOfColumns</span><span class="token punctuation">:</span> 12<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token selector">.grid</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token property">grid-template-columns</span><span class="token punctuation">:</span> <span class="token function">repeat</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--noOfColumns<span class="token punctuation">)</span><span class="token punctuation">,</span> 1fr<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>We don’t need to write the entire property value whenever we want to update the number of columns – we could use custom properties. This is a relatively simple example, but it might be much more useful if we have a more complex grid. And the technique could apply to things like track size or item placement too.</p>
<h2>Vendor prefixes</h2>
<p>Some properties (like <code>clip-path</code>) still require vendor prefixes in some browsers – although thankfully that number is going down. If you need to write a vendor prefix and then you want to change the property value, you need to make sure you change it on the prefixed property too. With custom properties we could instead write:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.some-element</span> <span class="token punctuation">{</span>
<span class="token property">--clip</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>0 0<span class="token punctuation">,</span> 100% 0<span class="token punctuation">,</span> 50% 100%<span class="token punctuation">,</span> 0 100%<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">-webkit-clip-path</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--clip<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--clip<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Now we only have one place we need to change it.</p>
<h2>Conclusion</h2>
<p>These are far from the only uses for custom properties, but they’re one that I typically find myself reaching for within my workflow, and can help make your code more efficient and maintainable. No doubt you’ll discover plenty more uses of your own!</p>
Beautiful Scrolling Experiences – Without Libraries2019-12-06T00:00:00Zhttps://css-irl.info/beautiful-scrolling-experiences-without-libraries/<p>Published on 24Ways</p>
Re-creating the ‘His Dark Materials’ Logo in CSS2019-12-05T00:00:00Zhttps://css-irl.info/recreating-the-his-dark-materials-logo-in-css/<p>I love the new <a href="https://www.bbc.co.uk/programmes/p071x4yx">BBC adaptation</a> of Philip Pullman’s <em>His Dark Materials</em> trilogy of novels. There’s also some pretty nice graphic design to appreciate. I’m a sucker for a beautiful title sequence (I need to watch the opening credits of <em>Game of Thrones</em> in full, every single time), and this one certainly fits the bill, as well as the striking logo. After watching a recent episode, I thought I’d have a go at re-creating this logo with CSS!</p>
<figure>
<img src="https://css-irl.info/recreating-the-his-dark-materials-logo-01.jpg" alt="His Dark Materials logo as created in CSS" />
<figcaption>My version of the logo created with CSS</figcaption>
</figure>
<h2>Typography</h2>
<p>Although it’s essentially a plain text logo, there’s plenty to unpack when it comes to building it for the web. The logo consists of two font weights, and most notably, is “fractured” into two parts by a diagonal slash through the middle. The text on the left of the fracture consists of a regular font weight, with the exception of the first word (“His”). The text on the right, and the first word, use a bold variant of the same font.</p>
<p>I don’t know what the actual font used in the logo is, but a few people on Twitter have suggested it might be <a href="https://houseind.com/hi/neutraface">Neutraface</a> by House Industries. I opted against paying $125 for the purpose of a fun demo, so I’ve substituted this for Josefin Sans, available from Google Fonts.</p>
<h2>HTML</h2>
<p>I wanted to build the logo in a way that would ensure it works on any background – that is to say, the background would be visible through the transparent areas of the logo – so hiding things with black gradients or pseudo elements was a no-no.</p>
<p>Although this is just a fun exercise, I wanted to avoid duplicating the HTML if possible. One (possibly easier) path would have been to use two spans with the same text inside and <code><h1></code>, like this:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>His Dark Materials<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">aria-hidden</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>His Dark Materials<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></code></pre>
<p>I could then use <code>position: absolute</code> on the second item to superimpose it on the first, and style them independently. There’s nothing inherently <em>wrong</em> about this – <code>aria-hidden</code> ensures the text won’t be read out twice by a screenreader –, but I preferred to keep it to a single text element if I could. I decided to make use of pseudo elements (<code>::before</code> and <code>::after</code>) and use the <code>content</code> property instead.</p>
<p>We can use <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties">CSS custom properties</a> to duplicate the text content into the pseudo elements:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">--text</span><span class="token punctuation">:</span> <span class="token string">'His Dark Materials'</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>His Dark Materials<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></code></pre>
<pre class="language-css"><code class="language-css"><span class="token selector">h1::before,
h1::after</span> <span class="token punctuation">{</span>
<span class="token property">content</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--text<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>This give us (effectively) two more “copies” of our text to work with. I put this in inverted commas because they’re not actually copies of the HTML, only the text content – and they’re not selectable or, for that matter, accessible on their own. But that’s fine, because our text content still exists in accessible form inside the <code><h1></code>.</p>
<p>If we position these absolutely, then they will overlay the original heading and we can style them individually.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">h1::before,
h1::after</span> <span class="token punctuation">{</span>
<span class="token property">content</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--text<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">position</span><span class="token punctuation">:</span> absolute<span class="token punctuation">;</span>
<span class="token property">top</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token property">left</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token property">width</span><span class="token punctuation">:</span> 100%<span class="token punctuation">;</span>
<span class="token property">text-align</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span>
<span class="token property">color</span><span class="token punctuation">:</span> white<span class="token punctuation">;</span>
<span class="token property">z-index</span><span class="token punctuation">:</span> -1<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h2>CSS</h2>
<p>Now we can use <code>clip-path()</code> on the pseudo elements to create the fracture effect:</p>
<pre class="language-scss"><code class="language-scss"><span class="token selector">h1 </span><span class="token punctuation">{</span>
<span class="token property">--high</span><span class="token punctuation">:</span> 80%<span class="token punctuation">;</span>
<span class="token property">--low</span><span class="token punctuation">:</span> 20%<span class="token punctuation">;</span>
<span class="token property">--gap</span><span class="token punctuation">:</span> 0.5rem<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">h1::after </span><span class="token punctuation">{</span>
<span class="token property">-webkit-clip-path</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>
<span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--high<span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token function">var</span><span class="token punctuation">(</span>--gap<span class="token punctuation">)</span><span class="token punctuation">)</span> 0<span class="token punctuation">,</span>
100% 0<span class="token punctuation">,</span>
100% 100%<span class="token punctuation">,</span>
<span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--low<span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token function">var</span><span class="token punctuation">(</span>--gap<span class="token punctuation">)</span><span class="token punctuation">)</span> 100%
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>
<span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--high<span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token function">var</span><span class="token punctuation">(</span>--gap<span class="token punctuation">)</span><span class="token punctuation">)</span> 0<span class="token punctuation">,</span>
100% 0<span class="token punctuation">,</span>
100% 100%<span class="token punctuation">,</span>
<span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--low<span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token function">var</span><span class="token punctuation">(</span>--gap<span class="token punctuation">)</span><span class="token punctuation">)</span> 100%
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">h1::before </span><span class="token punctuation">{</span>
<span class="token property">font-weight</span><span class="token punctuation">:</span> 400<span class="token punctuation">;</span>
<span class="token property">-webkit-clip-path</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>
<span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--high<span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token function">var</span><span class="token punctuation">(</span>--gap<span class="token punctuation">)</span><span class="token punctuation">)</span> 0<span class="token punctuation">,</span>
<span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--low<span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token function">var</span><span class="token punctuation">(</span>--gap<span class="token punctuation">)</span><span class="token punctuation">)</span> 100%<span class="token punctuation">,</span>
0 100%<span class="token punctuation">,</span>
0 0
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>
<span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--high<span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token function">var</span><span class="token punctuation">(</span>--gap<span class="token punctuation">)</span><span class="token punctuation">)</span> 0<span class="token punctuation">,</span>
<span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--low<span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token function">var</span><span class="token punctuation">(</span>--gap<span class="token punctuation">)</span><span class="token punctuation">)</span> 100%<span class="token punctuation">,</span>
0 100%<span class="token punctuation">,</span>
0 0
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>The <code>::before</code> pseudo element is clipped diagonally so that only the left portion is visible, and <code>::after</code> is clipped so that the reverse is visible, allowing a small gap between then (which will create the slash effect). As some browsers still require clip-path` to be prefixed, we can leverage custom properties to cut down on our overall code:</p>
<pre class="language-scss"><code class="language-scss"><span class="token selector">h1 </span><span class="token punctuation">{</span>
<span class="token property">--high</span><span class="token punctuation">:</span> 80%<span class="token punctuation">;</span>
<span class="token property">--low</span><span class="token punctuation">:</span> 20%<span class="token punctuation">;</span>
<span class="token property">--gap</span><span class="token punctuation">:</span> 0.5rem<span class="token punctuation">;</span>
<span class="token property">--clipLeft</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>
<span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--high<span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token function">var</span><span class="token punctuation">(</span>--gap<span class="token punctuation">)</span><span class="token punctuation">)</span> 0<span class="token punctuation">,</span>
<span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--low<span class="token punctuation">)</span> <span class="token operator">-</span> <span class="token function">var</span><span class="token punctuation">(</span>--gap<span class="token punctuation">)</span><span class="token punctuation">)</span> 100%<span class="token punctuation">,</span>
0 100%<span class="token punctuation">,</span>
0 0
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">--clipRight</span><span class="token punctuation">:</span> <span class="token function">polygon</span><span class="token punctuation">(</span>
<span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--high<span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token function">var</span><span class="token punctuation">(</span>--gap<span class="token punctuation">)</span><span class="token punctuation">)</span> 0<span class="token punctuation">,</span>
100% 0<span class="token punctuation">,</span>
100% 100%<span class="token punctuation">,</span>
<span class="token function">calc</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--low<span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token function">var</span><span class="token punctuation">(</span>--gap<span class="token punctuation">)</span><span class="token punctuation">)</span> 100%
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">h1::before </span><span class="token punctuation">{</span>
<span class="token property">font-weight</span><span class="token punctuation">:</span> 400<span class="token punctuation">;</span>
<span class="token property">-webkit-clip-path</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--clipLeft<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--clipLeft<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">h1::after </span><span class="token punctuation">{</span>
<span class="token property">-webkit-clip-path</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--clipRight<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">clip-path</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--clipRight<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<aside>
<p>`path()` is now supported as a clip-path property in Firefox, which allows for an SVG path syntax. I don’t know if that would help us here as I haven’t had the chance to play around with it.</p>
</aside>
<p>The original text is still visible underneath the clipped pseudo elements, so I’m setting the colour to transparent. The pseudo elements also have a lower z-index — that way the text will still be selectable, but the viewer will only see the clipped text below.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">h1</span> <span class="token punctuation">{</span>
<span class="token property">color</span><span class="token punctuation">:</span> transparent<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Lastly, while most of the text on the left of the slash is in the regular font weight, the first word needs to be bold. Unfortunately we can’t style this as a separate entity using <code>content</code>, so it needs a bit of hackery, as well as altering the markup to allow us to select the first word (in the absence of a <code>::first-word</code> selector!):</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">--text</span><span class="token punctuation">:</span> <span class="token string">'His Dark Materials'</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>His<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span> Dark Materials<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></code></pre>
<p>Now I can make the text of the first word visible and set the font weight to bold, which effectively hides the corresponding pseudo element content:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">h1 > span</span> <span class="token punctuation">{</span>
<span class="token property">font-weight</span><span class="token punctuation">:</span> 700<span class="token punctuation">;</span>
<span class="token property">color</span><span class="token punctuation">:</span> white<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>I’m not keen on this solution, as it is very limited. If we had a case where the first word needed to be styled in a different font, or a lighter weight, then hiding the pseudo element content wouldn’t work. But it’s good enough for our purpose this time.</p>
<p>Check out the full demo:</p>
<iframe height="378" style="width: 100%;" scrolling="no" title="His Dark Materials TV series logo with CSS" src="https://codepen.io/michellebarker/embed/yLLGVMQ?height=378&theme-id=default&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/yLLGVMQ">His Dark Materials TV series logo with CSS</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
A Layout Trick for Building a Contact List2019-11-16T00:00:00Zhttps://css-irl.info/a-layout-trick-for-building-a-contact-list/<p>I recently needed to build a design for a contact list that looks like this:</p>
<figure>
<img src="https://css-irl.info/a-layout-trick-for-building-a-contact-list.png" alt="Email address and telephone number links with accompanying icons on the right" />
</figure>
<p>It consists of an email address and telephone number (with the potential to add more contact types), each with an icon on the right and text on the left. Each item needed to be a link (including the icon). The important thing was that the width of the items should be determined by the <em>longest</em> item. In the case of the image above, the item with the longest content would be the email address, so the second item (the telephone number) would need to be the same width as this, even though its content is shorter.</p>
<p>Flex or block items by default take up 100% of the parent width. Hard-coding a width on the items would not be a desirable solution, as the length of the content may be unknown.</p>
<h2>Solving it with Grid</h2>
<p>I didn’t quite know how I would tackle this, but I had a hunch that using <code>max-content</code> with CSS Grid might provide the solution. It turns out I was right. If we set the following CSS properties on the list, then both items will be sized to the width of the longest item:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.contact-list</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token property">grid-template-columns</span><span class="token punctuation">:</span> <span class="token function">minmax</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> max-content<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Using the <code>grid-template-columns</code> property, this sets a single column for the grid, which has a minimum width of 0 and a maximum width of <code>max-content</code>. Referring to <a href="https://www.w3.org/TR/css-sizing-3/#max-content-inline-size">the specification</a> for <code>max-content</code>, this is:</p>
<blockquote>
<p>A box’s “ideal” size in a given axis when given infinite available space. Usually this is the smallest size the box could take in that axis while still fitting around its contents, i.e. minimizing unfilled space while avoiding overflow.</p>
</blockquote>
<p>The <code>0</code> value in the <code>minmax()</code> function doesn’t matter too much – it could in fact be and fixed value below the maximum content size. If we know that we never want our list items to be narrower than 5rem, then we might put that value in here.</p>
<h2>Alternative solutions</h2>
<p>I shared this tip <a href="https://twitter.com/CSSInRealLife/status/1195306788612190210?s=20">on Twitter</a> as I thought it might be useful. It got a much bigger response than I expected, so it seems this might be handy for a lot of people. A couple of people pointed out that you could also achieve the same result with <code>inline-block</code> or <code>inline-flex</code>. (Thanks <a href="https://twitter.com/htmlvv">@htmlvv</a>, <a href="https://twitter.com/ripcorddesign">@ripcorddesign</a> and <a href="https://twitter.com/hack_nug">@hack_nug</a>!) I’ve prepared a demo showing the different possible solutions:</p>
<iframe height="371" style="width: 100%;" scrolling="no" title="Different ways to force two list items to take the width of the longest" src="https://codepen.io/michellebarker/embed/gOOQyyL?height=371&theme-id=default&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/gOOQyyL">Different ways to force two list items to take the width of the longest</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>When using <code>inline-block</code> or <code>inline-flex</code> the browser adds a top and bottom margin, which you need to set to 0 (unless it’s desirable, of course). With <code>inline-flex</code> you also need to add <code>flex-direction: column</code>, so overall that option has the most lines of code:</p>
<pre class="language-css"><code class="language-css"><span class="token comment">/* Inline-block */</span>
<span class="token selector">.contact-list--inline-block</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> inline-block<span class="token punctuation">;</span>
<span class="token property">margin</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">/* Inline-flex */</span>
<span class="token selector">.contact-list--inline-flex</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> inline-flex<span class="token punctuation">;</span>
<span class="token property">flex-direction</span><span class="token punctuation">:</span> column<span class="token punctuation">;</span>
<span class="token property">margin</span><span class="token punctuation">:</span> 0<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h3>Pros and cons</h3>
<p>Both these solutions have the advantage over Grid of better browser support. However, support for Grid is very good, so the only place this would really be an issue is Internet Explorer. In the context of progressive enhancement, this works totally fine, at least for my use case: in browsers that don’t support Grid, each item would simply be 100% width. Not as elegant, but perfectly functional.</p>
<p>The other two solutions feel a little more hacky to me – less intentional, and not really using the properties as designed. But we do plenty of hacky stuff with CSS every day, so it’s definitely not a problem. The Grid solution is the one I implemented at the time, so I’m going to stick with it – and it’s handy to know that it can also work in the context of a larger grid if needed. But feel free to pick the one that works for you!</p>
Part 3: Building Our Sass Architecture2019-10-28T00:00:00Zhttps://css-irl.info/a-modern-front-end-workflow-part-3/<figure>
<img src="https://css-irl.info/a-modern-front-end-workflow-03_01.png" alt="Sass logo on a pink gradient background" />
</figure>
<p>In the previous two articles we went through configuring a project starter repository using NPM scripts and <a href="https://parceljs.org/">Parcel</a>. I tend to employ more or less the same Sass architecture for every project, so I want my boilerplate to include the SCSS files and folders I need to get started writing code straight away. This is my preferred architecture, loosely based on <a href="https://csswizardry.com/">Harry Roberts’</a> <a href="https://www.hongkiat.com/blog/inverted-triangle-css-web-development/">ITCSS</a> (Inverted Triangle CSS), and our boilerplate at my previous agency, <a href="https://ournameismud.co.uk/">Mud</a>:</p>
<ol>
<li>
<p><strong>Config</strong> This typically contains three files: It’s where I define all the Sass variables, mixins and function for use throughout the project. I generally use a single file for my variables, covering breakpoints, colours, spacing and anything else. But there’s no reason the variables file couldn’t be broken up into several files, which might be a good idea for large projects.</p>
</li>
<li>
<p><strong>Base</strong> Pretty much everything in here involves writing styles on element selectors, rather than classes. It includes any resets (e.g. <code>* { box-sizing: border-box; }</code>), and base styles for typography and form elements, some of which may be over-ridden at the component level later on.</p>
</li>
<li>
<p><strong>Objects</strong> Any small, reusable pieces of UI, which could appear in multiple components, belong here. I start with a <em>buttons.scss</em> file because, well, pretty much every project has buttons! But I only tend to add others when I need them.</p>
</li>
<li>
<p><strong>Globals</strong> I like to keep any components that will be used on every page, such as the header and footer, in a separate folder from the rest of the components. I also add generic layout classes in here – if I have a grid that I want to use in a lot of places, for example. I prefer to use classes rather than mixins for those, as it’s one fewer level of abstraction.</p>
</li>
<li>
<p><strong>Components</strong> This is for the larger chunks of UI, such as hero sections, cards, media objects and more. It’s where the bulk of my CSS will be written, but I leave it empty to begin with so that I can add individual component files as and when I need them.</p>
</li>
<li>
<p><strong>Utilities</strong> This is for single-purpose, reuseable atomic (or utility) classes that could be applied to any element. E.g. The following could be used to set vertical padding on all elements that have this class applied:</p>
</li>
</ol>
<pre class="language-scss"><code class="language-scss"><span class="token selector">.padding-v </span><span class="token punctuation">{</span>
<span class="token property">padding-top</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--pad<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">padding-bottom</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--pad<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>I tend not to use too many of these, so I’m going to leave this file empty until I need it.</p>
<p>So, to finish off our project starter, I’m going to add the following file structure to the <em>src/scss</em> directory:</p>
<pre><code>01-config
_variables.scss
_functions.scss
_mixins.scss
02-base
_resets.scss
_typography.scss
_forms.scss
03-objects
_buttons.scss
04-globals
_header.scss
_footer.scss
_layout.scss
05-components
06-utilities
</code></pre>
<p>You might notice each folder has a numerical prefix – this is so that when viewed in the file system the visual order will reflect the import order. This will make it much quicker and easier to find the file I want, and will help avoid any confusion.</p>
<p>It’s likely you’ll have your own preferred Sass architecture, so I would recommend adding at least a basic folder structure to your boilerplate – or feel free to tweak this one. Even if you use an atomic CSS framework (like <a href="https://tailwindcss.com/">Tailwind</a>), the chances are you might need to write some CSS yourself, so having the architecture in place makes sense.</p>
<h2>Conclusion</h2>
<p>This concludes the mini-series on A Modern Front-end Workflow. We’ve learnt about installing NPM packages, writing scripts to run tasks, using Parcel for easier automation, and adding a basic Sass architecture. <a href="https://github.com/mbarker84/parcel-starter">This starter repository</a> includes everything we covered. Feel free to clone it or fork it – and add to it – for your own projects.</p>
<h2>See other articles in this series</h2>
<ul>
<li><a href="https://css-irl.info/a-modern-front-end-workflow-part-1/">Building a Project Starter with NPM Scripts</a></li>
<li><a href="https://css-irl.info/a-modern-front-end-workflow-part-2/">Module Bundling with Parcel</a></li>
</ul>
Part 2: Module Bundling with Parcel2019-10-28T00:00:00Zhttps://css-irl.info/a-modern-front-end-workflow-part-2/<figure>
<img src="https://css-irl.info/a-modern-front-end-workflow-02_01.png" alt="Parcel logo on a purple gradient background" />
</figure>
<p><a href="https://parceljs.org/">Parcel</a> purports to be a “zero-config” alternative to <a href="https://webpack.js.org/">Webpack</a>, a popular Javascript module bundler. A module bundler takes separate, reusable JS files (or modules) and “bundles” them into a single file to be served to the browser, as well as minifying the output. This can improve website performance, as the browser doesn’t need to load a bunch of files individually. This in itself is very useful, but Parcel also takes care of other tasks for us out of the box, including:</p>
<ul>
<li>Running a local server</li>
<li>Building and minifying HTML, CSS and assets</li>
<li>Transpiling Javascript</li>
<li>Live reloading</li>
<li>Code splitting</li>
</ul>
<p>Using Parcel, we can do everything we already did in the <a href="https://css-irl.info/a-modern-front-end-workflow-part-1">previous tutorial</a> (and much more!), while writing fewer scripts.</p>
<h3>Creating a Parcel project</h3>
<p>Let’s create a new project structure, similar to the one in the previous article:</p>
<pre><code>my-awesome-project
src
icons
images
js
scss
index.html
node_modules
package.json
</code></pre>
<p>(This time I’m including any HTML files in the <em>src</em> directory, because they’ll get compiled too when we build the project.)</p>
<p>We’ll start by installing Parcel as a dependency, and we’ll also install <em>node-sass</em> while we’re at it (multiple packages can be listed to install them at the same time):</p>
<pre><code>npm install parcel-bundler node-sass --save-dev
</code></pre>
<p>(If you’re creating a new project, don’t forget to run <code>npm init</code> first.)</p>
<p>We’ll need to change the file path of our stylesheet in <em>index.html</em>. We only need to tell Parcel the relative path of our source file, as the actual path for use in production will be generated by Parcel at buildtime.</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>./scss/styles.scss<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span></code></pre>
<p>Parcel comes with all the commands we need to run and build our project. Simply running <code>parcel</code> plus the path to our <em>index.html</em> file is enough to run the project and watch for changes:</p>
<pre><code>parcel src/index.html
</code></pre>
<p>We could also (optionally) tell Parcel which port to use, and instruct it to open our website in a new browser tab whenever we start it up. The following command instructs it to use port 3000, and using the <code>--open</code> flag it will open in a new tab:</p>
<pre><code>parcel src/index.html -p 3000 --open
</code></pre>
<p>This is a lot to type every time we want to run the project, so I like to write an NPM script for this:</p>
<pre class="language-json"><code class="language-json"><span class="token property">"scripts"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"start"</span><span class="token operator">:</span> <span class="token string">"parcel src/index.html -p 3000 --open"</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span></code></pre>
<p>Now we only need to type <code>npm start</code>.</p>
<p>The second of parcel’s commands builds the website for production:</p>
<pre><code>parcel build src/index.html
</code></pre>
<p>Again, we can write a script to execute this command too:</p>
<pre class="language-json"><code class="language-json"><span class="token property">"scripts"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"start"</span><span class="token operator">:</span> <span class="token string">"parcel src/index.html -p 3000 --open"</span><span class="token punctuation">,</span>
<span class="token property">"build"</span><span class="token operator">:</span> <span class="token string">"parcel build src/index.html"</span>
<span class="token punctuation">}</span></code></pre>
<p>Parcel’s <code>build</code> command builds all the assets to a <em>dist</em> folder, but it doesn’t clean the folder out each time it’s run, so you can end up with duplicate files. I like to run a clean-up command before running a production build, so that we can be sure the only files built in the <em>dist</em> directory are the ones necessary to our project.</p>
<pre class="language-json"><code class="language-json"><span class="token property">"scripts"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"start"</span><span class="token operator">:</span> <span class="token string">"parcel src/index.html -p 3000 --open"</span><span class="token punctuation">,</span>
<span class="token property">"clean"</span><span class="token operator">:</span> <span class="token string">"rm -rf dist/*"</span><span class="token punctuation">,</span>
<span class="token property">"build:parcel"</span><span class="token operator">:</span> <span class="token string">"parcel build src/index.html"</span><span class="token punctuation">,</span>
<span class="token property">"build"</span><span class="token operator">:</span> <span class="token string">"npm run clean && npm run build:parcel"</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span></code></pre>
<p>I’ve made a few small changes to our scripts:</p>
<ol>
<li>The “clean” script removes everything in the <em>dist</em> folder</li>
<li>I’ve renamed the previous “build” script “build:parcel”.</li>
<li>Now the <code>build</code> command runs the “clean” script followed by the “build:parcel” script.</li>
</ol>
<p>Now go ahead and run <code>npm start</code>. Parcel should open your site in the browser at <a href="https://localhost:3000/">https://localhost:3000</a> and reload the page when you make any changes to your SCSS or HTML. We don’t need Browsersync, and we don’t need to write any additional scripts.</p>
<h3>Adding plugins</h3>
<p>We can add plugins (which are themselves NPM packages) to optimise images and create SVG sprites:</p>
<pre><code>npm install parcel-plugin-imagemin parcel-plugin-svg-sprite
</code></pre>
<p>For the <a href="https://github.com/DeMoorJasper/parcel-plugin-imagemin">parcel-plugin-imagemin</a> package to take effect, we need to add a config file. Add the following to a file called <em>imagemin.config.js</em> in the project root:</p>
<pre class="language-js"><code class="language-js">module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span>
<span class="token literal-property property">gifsicle</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token literal-property property">optimizationLevel</span><span class="token operator">:</span> <span class="token number">2</span><span class="token punctuation">,</span> <span class="token literal-property property">interlaced</span><span class="token operator">:</span> <span class="token boolean">false</span><span class="token punctuation">,</span> <span class="token literal-property property">colors</span><span class="token operator">:</span> <span class="token number">10</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token literal-property property">jpegtran</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token literal-property property">progressive</span><span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span> <span class="token literal-property property">arithmetic</span><span class="token operator">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token literal-property property">pngquant</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token literal-property property">quality</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token number">0.25</span><span class="token punctuation">,</span> <span class="token number">0.5</span><span class="token punctuation">]</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token literal-property property">svgo</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token literal-property property">plugins</span><span class="token operator">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span> <span class="token literal-property property">removeViewBox</span><span class="token operator">:</span> <span class="token boolean">false</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token literal-property property">cleanupIDs</span><span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span><span class="token punctuation">]</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token literal-property property">webp</span><span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token literal-property property">quality</span><span class="token operator">:</span> <span class="token number">10</span> <span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span></code></pre>
<p>You can adjust the options for the desired level optimisation (see the plugin’s documentation). <em>parcel-plugin-svg-sprite</em> should just work out-of-the-box – run the project and try adding some SVG icons to the <em>src/icons</em> directory. You should then be able to use any of them in your HTML with the SVG <code><use></code> element:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>svg</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>use</span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>icons/my-icon.svg<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>svg</span><span class="token punctuation">></span></span></code></pre>
<h2>Transpiling</h2>
<p>I like to write my JS using ES2015 syntax. <a href="https://babeljs.io/">Babel</a>, a transpiler, converts modern Javascript to a syntax that can be read by older browsers – meaning you can write the latest JS code and have it work everywhere. That’s a pretty useful thing to include in a project starter.</p>
<p>Babel has a lot of different plugins and configuration options, and wading through them can feel a bit daunting. But there’s a handy package called <em>preset-env</em> that takes care of transforming all the features of ES2015 (think arrow functions, destructuring, spread operators), which suits me just fine, so let’s install that.</p>
<pre><code>npm install @babel/core @babel/preset-env --save-dev
</code></pre>
<p>You can add other plugins if you want to configure Babel to suit your specific needs (e.g. if you’re using React, Vue or another framework).</p>
<p>Now we need to add a config file in the project root:</p>
<pre><code>touch .babelrc
</code></pre>
<p>If we’re happy using the default config options then we don’t need to add much at all to our config file. The following will suffice:</p>
<pre><code>{
"presets": ["@babel/preset-env"]
}
</code></pre>
<p>The Babel documentation has more information on <a href="https://babeljs.io/docs/en/babel-preset-env">config options</a> should you need it.</p>
<p>Parcel runs Babel for us automatically, so once we’ve installed it and created our config file we’re good to go – our code will be transpiled whenever we run <code>npm run build</code>.</p>
<h2>Example</h2>
<p>I’ve created a <a href="https://github.com/mbarker84/parcel-starter">starter respository</a> using the same process as in this article – feel free to clone or fork it and use it for your projects.</p>
<h2>Coming up</h2>
<p>So far in this series we’ve seen how to build a starter for very simple project using NPM scripts, and learnt how to use Parcel to handle more complex tasks with minimal configuration. In the third article we’ll add a simple Sass architecture to help us get up and running quickly with writing styles.</p>
<h2>See other articles in this series</h2>
<ul>
<li><a href="https://css-irl.info/a-modern-front-end-workflow-part-1/">Building a Project Starter with NPM Scripts</a></li>
<li><a href="https://css-irl.info/a-modern-front-end-workflow-part-3/">Building Our Sass Architecture</a></li>
</ul>
Part 1: Building a Project Starter with NPM Scripts2019-10-28T00:00:00Zhttps://css-irl.info/a-modern-front-end-workflow-part-1/<figure>
<img src="https://css-irl.info/a-modern-front-end-workflow-01_01.png" alt="NPM logo on blue gradient background" />
</figure>
<p>When it comes to building a simple front-end project, how do you get started? What are the tools you need? I suspect everyone will have a different answer. Do you start with a (JS or CSS) framework, or off-the-shelf boilerplate? Perhaps you use a task runner (like <a href="https://gulpjs.com/">Gulp</a> to orchestrate your project’s needs. Or do you start simple, with just HTML and a CSS file?</p>
<p>The front-end tooling landscape can be confusing, and at times overwhelming – and when you’re dedicating your time to learning HTML, CSS and Javascript, it feels like yet another thing you need to make time to learn. In this series of articles I want to help developers understand some of the tools and methodologies that have become commonplace for building web projects. Over the next three articles we’ll build a simple project starter (or boilerplate) together. We’ll cover:</p>
<ol>
<li>An introduction to using NPM scripts (this article) for compiling Sass, running a server and live reloading.</li>
<li>Getting up and running with <a href="https://parceljs.org/">Parcel</a>, a minimal-config application bundler.</li>
<li>Building out a reusable Sass architecture</li>
</ol>
<p>Feel free to skip over the parts you’re already familiar with.</p>
<h2>Why do we need a project starter repository?</h2>
<p>I’ve written previously on this blog about <a href="https://css-irl.info/building-a-dependency-free-site/">keeping things simple</a> and building dependency-free — and for a basic, minimal site, this approach has a lot to recommend it. But the vast majority of my projects would benefit from a bit more tooling. In any given project, it’s likely that at the very least I’ll want to:</p>
<ul>
<li>Run a local server</li>
<li>Compile SCSS to CSS, and minify the output</li>
<li>Live reload (show changes in the browser without the need for manual refresh)</li>
<li>Optimise images</li>
<li>Create SVG icon sprites</li>
</ul>
<p>In larger projects, there are plenty more tooling options we could add into the mix to help us build performant, accessible websites. We might want module bundling, code splitting and transpiling. On the CSS side, perhaps we’d like to inline our critical CSS, or purge unused selectors.</p>
<p>If you don’t know what some of these words mean, you’re not alone! Front-end development has got a lot more complex in recent years, and it can be hard to keep abreast of the constant changes to best practices. One article that has really helped me understand the vast tooling landscape that these days falls into the realm of front-end development is <a href="https://medium.com/the-node-js-collection/modern-javascript-explained-for-dinosaurs-f695e9747b70">Modern Javascript Explained For Dinosaurs</a>. Although a couple of years old, this article is still extremely relevant, and explains succinctly how Javascript has evolved to become such a vital part of our workflow.</p>
<p>All this takes time to set up and configure, and to do it from scratch every time we start a new project wouldn’t be ideal. Which is why it’s useful to have a starter repository that we can clone or download, with everything we need to start coding straight away.</p>
<h2>Choosing our tools</h2>
<p>I’m not a person who loves spending time setting up complex tooling. I want my tools to demand as little time from me as possible, so that I can concentrate on the things I love doing! Whilst I’ve used Gulp in the past, it now seems a less necessary part of the toolchain: virtually all dependencies can be installed via NPM and configuring them with NPM scripts is no more difficult than configuring them with Gulp. So using a task runner seems a bit redundant, and would only add an extra dependency to the project.</p>
<p>The tools I’ve chosen here are a personal preference, and suit the kind of projects I like to build. They’re not necessarily everyone’s choice, and there are plenty of different ways to do things. But I hope this tutorial will help you get a bit more familiar with some of the tools that have become popular among developers, so that you can make your own choices.</p>
<p>With that in mind, let’s begin building our project starter, and learn about the tools we’ll be using along the way. Feel free to skip over any parts you’re already familiar with.</p>
<h2>Installing Node.js</h2>
<p>The very first thing we need to do to get our project set up to work with NPM scripts is to make sure we have <a href="https://nodejs.org/">Node.js</a> installed globally. This sounds simple enough, but already things start to get a little more complicated when we realise there are a number of different ways to do this:</p>
<ul>
<li><a href="https://nodejs.org/">Download the latest version from the website</a> and follow the instructions for installation</li>
<li>Use a package manager like <a href="https://brew.sh/">Homebrew</a> for Mac, which allows us to update our node version with a simple command: <code>brew upgrade node</code>.</li>
<li>Using <a href="https://github.com/nvm-sh/nvm">NVM (Node Version Manager)</a>.</li>
</ul>
<p>NVM is my preferred option, as it allows us to easily upgrade our node version, see which version we’re currently running, list other installed versions or switch to another version using single commands. But it requires additional steps to install depending on your setup, which is beyond the scope of this particular article.</p>
<p>Once you have Node installed (by whichever method suits you), you can check the currently installed version by running <code>node -v</code>. (You might want to upgrade to the latest version.) If you’re using NVM you could (optionally) create a <em>.nvmrc</em> config file to ensure you always run the correct Node version for your project.</p>
<aside>
<h4>Tip: Check your Node version</h4>
<p>If you get an error when trying to run your project then it’s a good idea to check you’re running the right Node version. If in doubt, you can always delete your node_modules folder, install the latest version of Node, and run <code>npm install</code> again.</p>
</aside>
<h2>NPM</h2>
<p>Installing Node also installs <a href="https://www.npmjs.com/">NPM</a> (Node Package Manager). This is basically a huge library of open source Javascript development tools (or packages) that anyone can publish to. We have direct access to this library of tools and (for better or worse!) can install any of them in our projects.</p>
<h3>NPM or Yarn?</h3>
<p><a href="https://yarnpkg.com/lang/en/">Yarn</a> is an alternative package manager, similar to NPM, and almost as popular. In fact, many people consider it an improvement. It can be used in a similar way, to install dependencies. If you prefer to use Yarn over NPM, you can safely substitute NPM commands with the Yarn equivalent anywhere they’re used in this article.</p>
<aside>
Note, whether we use Yarn or NPM, it’s still NPM packages we’re installing – Yarn is just the CLI tool, there isn’t a rival Yarn package library. Just one of the many confusing things about the Javascript ecosystem!
</aside>
<h3>Initialising the project</h3>
<p>First, let’s create a new project folder, which we’ll (imaginitively) call <em>new-project</em>. Open the terminal, and inside that folder run:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">npm</span> init</code></pre>
<p>Running this command brings up several steps for initialising our project in the command line, such as adding a name and description. You can hit <kbd>Enter</kbd> to skip through each of these if you don’t want to complete them right away – we’ll be able to edit them later on. You’ll then see that a <em>package.json</em> file has been created, which should look something like this:</p>
<pre class="language-json"><code class="language-json"><span class="token punctuation">{</span>
<span class="token property">"name"</span><span class="token operator">:</span> <span class="token string">"project-starter"</span><span class="token punctuation">,</span>
<span class="token property">"version"</span><span class="token operator">:</span> <span class="token string">"1.0.0"</span><span class="token punctuation">,</span>
<span class="token property">"description"</span><span class="token operator">:</span> <span class="token string">""</span><span class="token punctuation">,</span>
<span class="token property">"main"</span><span class="token operator">:</span> <span class="token string">"index.js"</span><span class="token punctuation">,</span>
<span class="token property">"scripts"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"test"</span><span class="token operator">:</span> <span class="token string">"echo \"Error: no test specified\" && exit 1"</span>
<span class="token punctuation">}</span><span class="token punctuation">,</span>
<span class="token property">"author"</span><span class="token operator">:</span> <span class="token string">""</span><span class="token punctuation">,</span>
<span class="token property">"license"</span><span class="token operator">:</span> <span class="token string">"ISC"</span>
<span class="token punctuation">}</span></code></pre>
<p>This file contains all the information about our project, and is where we can edit the details that we just skipped through.</p>
<p>Any packages that we install from NPM will be automatically listed in the <em>package.json</em> file. It’s also where we’ll configure the scripts that will build and run our project. We’ll install some packages and configure these shortly, but first we’ll need a basic project architecture, and some files to work with.</p>
<h2>Project structure</h2>
<p>We’ll start with a folder structure that looks like this:</p>
<pre class="language-bash"><code class="language-bash">new-project
index.html
src
icons
images
js
scss
node_modules
package.json</code></pre>
<p>We’ve already generated the <code>node_modules</code> directory and <em>package.json</em> in the root of the project. We just need to create a directory called <em>src</em>, containing directories for images, JS, SCSS and icons, plus an <em>index.html</em> file.</p>
<h3>Creating our folder structure from the command line</h3>
<p>You could create the above folder structure manually, either in your text editor of choice or in your computer’s file system. But if you want to save time, you could do it from the terminal instead. In the root of the project, you could run:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">touch</span> index.html
<span class="token function">mkdir</span> src <span class="token operator">&&</span> <span class="token builtin class-name">cd</span> src
<span class="token function">mkdir</span> js scss images icons
<span class="token builtin class-name">cd</span> <span class="token punctuation">..</span>/</code></pre>
<p>Line by line, this code:</p>
<ol>
<li>Creates a new <em>index.html</em> file</li>
<li>Creates a new <em>src</em> directory and moves us into the newly-created directory</li>
<li>Creates directories inside <em>src</em> called <em>js</em>, <em>scss</em>, <em>images</em> and <em>icons</em>, and a file called <em>index.html</em>.</li>
<li>Brings us back up to the project root.</li>
</ol>
<p>Now let’s add the following to our <em>index.html</em> file so that we can see our site in the browser:</p>
<pre class="language-html"><code class="language-html"><span class="token doctype"><span class="token punctuation"><!</span><span class="token doctype-tag">DOCTYPE</span> <span class="token name">html</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>head</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">charset</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>UTF-8<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">name</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>viewport<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>width=device-width, initial-scale=1.0<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>meta</span> <span class="token attr-name">http-equiv</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>X-UA-Compatible<span class="token punctuation">"</span></span> <span class="token attr-name">content</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>ie=edge<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>title</span><span class="token punctuation">></span></span>Project starter<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>title</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>link</span> <span class="token attr-name">rel</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>stylesheet<span class="token punctuation">"</span></span> <span class="token attr-name">type</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>text/css<span class="token punctuation">"</span></span> <span class="token attr-name">href</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>dist/css/styles.css<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>head</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>Hello world!<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span></code></pre>
<h2>Installing dependencies</h2>
<p>Now that we have our basic folder structure, we can start to install some packages and write some NPM scripts that will let us build and view our website. The scripts we’re going to write will:</p>
<ol>
<li>Run a local server</li>
<li>Compile Sass to CSS</li>
<li>Watch for changes and reload the page whenever we update our HTML or CSS</li>
</ol>
<p>Let’s install the <a href="https://www.npmjs.com/package/node-sass">node-sass</a> package from NPM, which compiles <em>.scss</em> files to CSS. In the terminal run:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">npm</span> <span class="token function">install</span> node-sass --save-dev</code></pre>
<aside>
<h4>Update</h4>
<p>Since this post was written, Node Sass has been deprecated in favour of Dart Sass. I recommend using that instead, using <code>npm install sass --save-dev</code>. (<a href="https://sass-lang.com/install">See the docs for details</a>) 🙂</p>
</aside>
<p>Once this command has finished running, you should see a couple of new things:</p>
<ol>
<li>A directory called <code>node_modules</code> has been created</li>
<li>In the <em>package.json</em> file, <code>node-sass</code> is now listed in “devDependencies”.</li>
<li>Adds a <em>package-lock.json</em> file. This isn’t something we should ever need to touch.</li>
</ol>
<h3>Adding a .gitignore</h3>
<p>The <code>node_modules</code> directory is where the code for all of our project dependencies will live. The contents of this folder should <em>not</em> be committed to Github (or your favourite repository host), as installing just a few dependencies could result in hundreds of thousands of files! So the next thing we should do is add a <em>.gitignore</em> file in the project root:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">touch</span> .gitignore <span class="token operator">&&</span> <span class="token builtin class-name">echo</span> <span class="token string">"node_modules"</span> <span class="token operator">>></span> .gitignore</code></pre>
<p>This command creates the <em>.gitignore</em> file and adds <code>node_modules</code> to it. (Again, you can do this manually if you prefer.) Now we are safe in the knowledge that our packages will not be committed.</p>
<p>If we’re not committing these files, then how can we share our dependencies with other users? Well, this down to the <em>package.json</em> file. It tells us the name and version number of any dependencies we have installed. Anyone who clones or forks the project (including us, when we use it to start a new project) can simply run <code>npm install</code> and all the associated dependencies will be fetched and downloaded from NPM.</p>
<h3>Types of dependencies</h3>
<p>When we installed <em>node-sass</em> we ran the <code>install</code> command with the <code>--save-dev</code> flag. This installs the project as a “dev dependency”. Other packages may not require this command, and save a package under “dependencies” instead. The difference is that regular dependencies are <em>runtime</em> dependencies, whereas dev dependencies are <em>buildtime</em> dependencies. <em>node-sass</em> is required to build your project, so it’s a dev dependency. But something like, say, a carousel plugin, or framework that needs to be downloaded on the client side, (like React) would need to be a regular dependency.</p>
<p>Now we’ll also install <a href="https://www.npmjs.com/package/browser-sync">Browsersync</a> as a dev dependency. Browsersync will run a local server and reload the browser when our files change.</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">npm</span> <span class="token function">install</span> browser-sync --save-dev</code></pre>
<h2>Writing NPM scripts</h2>
<p>Now it’s time to write some scripts to run our project. We’re going to write these in the “scripts” section of our <em>package.json</em>.</p>
<h3>Sass to CSS</h3>
<p>NPM scripts consist of a key (the name of the script, which is what we would type in the terminal in order to run it) and a value – the script itself, which will be executed when we run the command. First we’ll write the script which compiles Sass to CSS. We’ll give it the name <strong>scss</strong> (we could name it anything we like) and add it to our “scripts” section:</p>
<pre class="language-json"><code class="language-json"><span class="token property">"scripts"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"scss"</span><span class="token operator">:</span> <span class="token string">"node-sass --output-style compressed -o dist/css src/scss"</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span></code></pre>
<p>The <em>node-sass</em> package contains some <a href="https://github.com/sass/node-sass">options</a>, some of which we’re defining here. We’re specifying the output style (“compressed”), the output directory (<em>dist/css</em>) and the source directory (<em>src/scss</em>), which is currently empty. Let’s create a source <em>.scss</em> file from the terminal:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">touch</span> src/scss/styles.scss</code></pre>
<p>Add a few styles to the newly-created file, then go back to the terminal and run:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">npm</span> run scss</code></pre>
<p>You should then see a new directory called <em>dist</em> has been created, containing your compiled CSS. Now, every time you make changes to your <em>styles.scss</em> file, you can run the script and those changes will be compiled.</p>
<h3>Live reloading</h3>
<p>Our first script is working great, but it’s not very useful yet, as every time we make changes to our code we need to got back to the terminal and run the script again. What we would be much better it to run a local server and see our changes reflected instantaneously in the browser. In order to do that we’ll write a script that uses Browsersync, which we’ve already installed.</p>
<p>First, let’s write the script that runs the server, which we’ll call <strong>serve</strong>:</p>
<pre class="language-json"><code class="language-json"><span class="token property">"scripts"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"scss"</span><span class="token operator">:</span> <span class="token string">"node-sass --output-style compressed -o dist/css src/scss"</span><span class="token punctuation">,</span>
<span class="token property">"serve"</span><span class="token operator">:</span> <span class="token string">"browser-sync start --server --files 'dist/css/*.css, **/*.html'"</span>
<span class="token punctuation">}</span></code></pre>
<p>In the <code>--files</code> option we’re listing the files that Browsersync should monitor. It will reload the page when any of these change. If we run this script now (<code>npm run serve</code>), it will start a local server and we can preview our web page by going to <a href="http://localhost:3000/">http://localhost:3000</a> in the browser.</p>
<h3>Watching for changes</h3>
<p>Currently we still need to run our <strong>scss</strong> script when we want to compile our Sass. What we need our scripts to do is:</p>
<ol>
<li>Watch our <em>src/scss</em> directory for changes.</li>
<li>When a change occurs, compile this to CSS in <em>dist/css</em>.</li>
<li>When <em>dist/css</em> is updated, reload the page.</li>
</ol>
<p>First we need to install an NPM package called <em>onchange</em>, to watch for changes to the source files:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">npm</span> <span class="token function">install</span> onchange --save-dev</code></pre>
<p>We can write NPM scripts that run other scripts. Let’s add the script that watches for changes and triggers our <strong>scss</strong> command to run:</p>
<pre class="language-json"><code class="language-json"><span class="token property">"scripts"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"scss"</span><span class="token operator">:</span> <span class="token string">"node-sass --output-style compressed -o dist/css src/scss"</span><span class="token punctuation">,</span>
<span class="token property">"serve"</span><span class="token operator">:</span> <span class="token string">"browser-sync start --server --files 'dist/css/*.css, **/*.html'"</span><span class="token punctuation">,</span>
<span class="token property">"watch:css"</span><span class="token operator">:</span> <span class="token string">"onchange 'src/scss' -- npm run scss"</span><span class="token punctuation">,</span>
<span class="token punctuation">}</span></code></pre>
<p>The <strong>watch:css</strong> script watches for changes using the <em>onchange</em> package (<em>src/scss</em>) and runs our <strong>scss</strong> script when changes occur.</p>
<h3>Combining scripts</h3>
<p>Now we need to run two commands in parallel: The <strong>serve</strong> command to run our server, and the <strong>watch:css</strong> command to compile our Sass to CSS, which will trigger the page reload. Using NPM scripts we can easily run commands consecutively using the <em>&&</em> operator:</p>
<pre class="language-json"><code class="language-json"><span class="token property">"scripts"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token comment">/*...*/</span>
<span class="token property">"start"</span><span class="token operator">:</span> <span class="token string">"npm run serve && npm run scss"</span>
<span class="token punctuation">}</span></code></pre>
<p>However, this won’t achieve what we want, as the script will wait until <em>after</em> the <strong>serve</strong> script has finished running before it begins running the <strong>scss</strong> script. If we go ahead and write this script, then run it in the terminal (<code>npm start</code>), then <code>npm run scss</code> won’t be triggered until we’ve stopped the server.</p>
<p>To enable us to run commands in parallel, we need to install another package. NPM has several options to choose from. The one I’ve picked is <a href="https://www.npmjs.com/package/npm-run-all">npm-run-all</a>:</p>
<pre class="language-bash"><code class="language-bash"><span class="token function">npm</span> <span class="token function">install</span> npm-run-all --save-dev</code></pre>
<p>The main options in this package (or at least, the ones we care about) are <strong>run-s</strong> and <strong>run-p</strong>. The former is for running sequentially, the latter is for running commands in parallel. Once we have installed this package, we can use it to write the script that runs both our <strong>serve</strong> and <strong>watch:css</strong> commands in parallel. (We’ll call it <strong>start</strong>.)</p>
<pre class="language-json"><code class="language-json"><span class="token property">"scripts"</span><span class="token operator">:</span> <span class="token punctuation">{</span>
<span class="token property">"scss"</span><span class="token operator">:</span> <span class="token string">"node-sass --output-style compressed -o dist/css src/scss"</span><span class="token punctuation">,</span>
<span class="token property">"serve"</span><span class="token operator">:</span> <span class="token string">"browser-sync start --server --files 'dist/css/*.css, **/*.html'"</span><span class="token punctuation">,</span>
<span class="token property">"watch:css"</span><span class="token operator">:</span> <span class="token string">"onchange 'src/scss' -- npm run scss"</span><span class="token punctuation">,</span>
<span class="token property">"start"</span><span class="token operator">:</span> <span class="token string">"run-p serve watch:css"</span>
<span class="token punctuation">}</span></code></pre>
<p>We now have a very basic starter project. We’ve written some scripts that allow us to simply type the command <code>npm start</code> to run a server, watch for changes, compile Sass to CSS and reload the page. An example repository can be found <a href="https://github.com/mbarker84/project-starter">here</a>.</p>
<p>We could now go ahead and install some packages and write scripts to automate some of our other tasks, such as optimising images, creating SVG sprites and uglifying JS. <a href="https://css-tricks.com/why-npm-scripts/">This CSS Tricks article</a> has a great rundown of a few more scripts you might like to add, as well as a <a href="https://github.com/damonbauer/npm-build-boilerplate">starter repository</a>. (Be aware, one or two of the packages included in the example have since been deprecated. You may need to search NPM for substitutes.)</p>
<p>This may serve us perfectly well for small projects, but the more tasks we want to run, the more scripts we’ll need to write, and orchestrating them all becomes more complex. So, in the next article we’ll look at how <a href="https://parceljs.org/">Parcel</a>, an application bundler, can automate a lot of these tasks for us with minimal configuration, and provide the tooling we need in order to build larger projects.</p>
<h2>See the next articles in this series</h2>
<ul>
<li><a href="https://css-irl.info/a-modern-front-end-workflow-part-2/">Module Bundling with Parcel</a></li>
<li><a href="https://css-irl.info/a-modern-front-end-workflow-part-3/">Building Our Sass Architecture</a></li>
</ul>
State of the Art CSS2019-10-23T00:00:00Zhttps://css-irl.info/state-of-the-art-css/<p>An article on new and upcoming CSS features for Web Designer magazine.</p>
Building a Scrapbook Layout with CSS Grid2019-09-30T00:00:00Zhttps://css-irl.info/building-a-scrapbook-layout-with-css-grid/<p>My son was recently tasked with the responsibility of looking after his pre-school class teddy bear for the week, which comes with the obligation to take said teddy bear out on adventures and add your memories to a scrapbook. I quite enjoyed creating this scrapbook layout, and it got me thinking about how I would build something like this with CSS Grid!</p>
<figure>
<img src="https://css-irl.info/building-a-scrapbook-layout_02.jpg" alt="Child’s preschool scrapbook spread titled ‘Hector’s Adventures with George’" />
</figure>
<h2>Compound grids</h2>
<p><a href="https://stuffandnonsense.co.uk/">Andy Clarke</a> delivered a fantastic talk, <em>Inspired by CSS Grid</em> at this <a href="https://2019.stateofthebrowser.com/">State of the Browser</a> conference, which was really illuminating for someone like me, a developer with design roots. In it he talked about ways in which ideas from print design can be applied to the web to create striking layouts, and how CSS Grid makes it not only possible, but much more straightforward than ever before. One of those principles was using compound grids.</p>
<p>Most of us are probably familiar with using grids in some way for web design and development. Virtually all website designs I’ve been handed to build have hung on a standard 12-column (or occasionally 24-column!) grid, with all columns an equal width. So far, so predictable.</p>
<p>Compound grids, on the other hand, are created by layering two or more grids. Juxtapositions such as a 5-column grid superimposed onto a 4-column grid produce rhythmic patterns, and open up more dynamic layout possibilities than a regular grid.</p>
<figure>
<img src="https://css-irl.info/building-a-scrapbook-layout_03a.png" alt="A 4-column grid over a 5-column grid" />
<figcaption><em>Fig 2</em> We start with a 4-column and a 5-column grid</figcaption>
</figure>
<figure>
<img src="https://css-irl.info/building-a-scrapbook-layout_04a.png" alt="4-column and 5-column grids superimposed, with the result shown below" />
<figcaption><em>Fig 3</em> The grids are superimposed, one on top of the other. The result is a compound grid.</figcaption>
</figure>
<p>This applies from a psychological as well as a technical point-of-view – it’s perfectly possibly to built a very ordinary layout despite using a compound grid, but something about having a more interesting grid to work with encourages the creative juices to flow! Andy has written a detailed article, <a href="https://www.smashingmagazine.com/2019/07/inspired-design-decisions-pressing-matters/">Inspired Design Decisions: Pressing Matters</a>, which explains compound grids (and more) in-depth. <a href="https://www.youtube.com/watch?v=eUeoLUjOUHw">His talk from State of the Browser</a> is also available to watch.</p>
<h3>Building a compound grid generator</h3>
<p>Compound grids translate really well to CSS Grid, as using <em>fr</em> units makes them very simple to implement. I love the idea of using compound grids in web design, but I felt the process of calculating them (especially the more complex the grid) could be a hindrance. I wanted a way to generate compound grids on demand so, inspired by Andy’s talk, I rolled up my sleeves and built a little tool to generate and visualise compound grids. Enter the number of columns for two different grids (with 10 columns as a maximum for any one grid) and the generator combines them, spitting out a resulting value which can be used in the <code>grid-template-columns</code> property using CSS Grid. For example, a grid with four columns plus a grid with five columns will generate the value <code>4fr 1fr 3fr 2fr 2fr 3fr 1fr 4fr</code>.</p>
<figure>
<img src="https://css-irl.info/building-a-scrapbook-layout_01.png" alt="Screenshot of the compound grid generator tool" />
</figure>
<p>This tool is <a href="https://codepen.io/michellebarker/full/zYOMYWv">on Codepen</a>, so feel free to use it or adapt it to suit your needs.</p>
<h2>Creating the grid for a scrapbook layout</h2>
<p>A compound grid is ideal for a scrapbook layout, where I wanted the layout to feel slightly unpredictable and haphazard but still maintain a sense of rhythm and balance. After a bit of experimentation with the compound grid generator, I settled on a 6/5 compound grid, which I felt gave me a good number of columns to play around with. That gives me an initial grid to work with:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.grid</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token property">grid-template-columns</span><span class="token punctuation">:</span> 5fr 1fr 4fr 2fr 3fr 3fr 2fr 4fr 1fr 5fr<span class="token punctuation">;</span>
<span class="token property">gap</span><span class="token punctuation">:</span> 1rem<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h3>Defining grid rows</h3>
<p>Defining the grid rows was trickier, and required a bit more trial-and-error. Each photo in the grid needs to overlap another. It was helpful to roughly draw the grid out on paper to understand how many rows I would need.</p>
<p>To maintain a sense of vertical rhythm, I decided the photos should each overlap by the same amount. I assigned this amount to a custom property so that it could be used thoughout the page and updated if necessary (<em>Fig 4</em>).</p>
<figure>
<img src="https://css-irl.info/building-a-scrapbook-layout_05a.png" alt="Grid with items overlapping vertically" />
<figcaption><em>Fig 4</em> Images overlap vertically</figcaption>
</figure>
<pre class="language-css"><code class="language-css"><span class="token selector">.grid</span> <span class="token punctuation">{</span>
<span class="token property">--verticalPadding</span><span class="token punctuation">:</span> 2rem<span class="token punctuation">;</span>
<span class="token property">--overlap</span><span class="token punctuation">:</span> 6rem<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Each image also has an accompanying paragraph of text. This needs to have sufficient space above and below, so that it doesn’t overlap or crash into the previous photo. This meant adding a grid row above and below the caption, which I think of as a “padding” row. Now each image would need to span at least four grid rows – images that overlap at the top <em>and</em> bottom would need to span five rows.</p>
<figure>
<img src="https://css-irl.info/building-a-scrapbook-layout_06a.png" alt="Grid showing padding rows between images and text blocks" />
<figcaption><em>Fig 5</em> “Padding” rows allow a minimum space to be maintained between the end of a text block and the start of the next image.</figcaption>
</figure>
<p>But we’re not quite done with building our grid yet: I decided to force an aspect ratio on the images themselves, just like real photos. Some would be landscape and others portrait. I wanted the grid layout to work regardless of the image aspect ratio or the length of text, so I needed my grid rows to be able to adapt.</p>
<p>Instead of using fixed values for the overlap and “padding” rows, we can make these tracks flexible by using <code>minmax()</code>. This will ensure those row tracks have a minimum size, but they will also expand if the content necessitates it.</p>
<pre><code>minmax(var(--padding, auto));
</code></pre>
<h2>Placing items</h2>
<p>Once we have our grid “scaffolding” in place, it’s time to place some items. One thing that can sometimes be tricky with CSS Grid is understanding the best way to place items on the grid for any given layout. We have a number of different options available to us – line numbers, the <em>span</em> keyword, named lines or named grid areas – and some work better than others in different situations. But there’s no right or wrong methodology, and it often comes down to finding the method that makes to most sense to you.</p>
<p>As long as the layout works, there’s no such thing as “doing it wrong”!</p>
<h3>Placement by grid line</h3>
<p>I often start by placing items using start and end values – usually start and end line numbers, but if I know the exact number of tracks an item needs to span then I’ll use that instead, treating it as a constant. I sometimes name grid lines for important “landmarks” (e.g. <code>wrapper-start</code> and <code>wrapper-end</code>), but I rarely go so far as to name grid lines or create grid areas for every item in the grid. A strategy that serves me very well is using negative grid lines for when I want to place an item relative to the end of the grid, which I have <a href="https://css-irl.info/negative-grid-lines">written about in a previous article</a>. I use negative grid lines most frequently on the column axis, as in most cases (for the grids I’m working with) the number of columns in a known, fixed value.</p>
<p>Placing an item from line <em>1</em> to line <em>-1</em> with the <code>grid-column</code> property, for example, would position that item spanning the entire column axis of our grid, from the first line to the last:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.item</span> <span class="token punctuation">{</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> 1 / -1<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Another case where I might be more inclined to name grid lines is for grids with a very large number of tracks. In this case we only have 10 column tracks, so for me placing items by line number on this axis feels manageable.</p>
<p>Using a mixture of positive and negative grid lines, and span values, it’s fairly straightforward to place items on the column axis. Switching on the grid inspector in the Firefox dev tools panel is very helpful, as it allows us to see the line numbers.</p>
<h3>Placement by grid area</h3>
<p>If we take a look at the grid we can see that we have quite a large number of rows to work with.</p>
<figure>
<img src="https://css-irl.info/building-a-scrapbook-layout_08.jpg" alt="Screenshot of grid rows" />
<figcaption><em>Fig 7</em> Screenshot from the Firefox grid inspector in the dev tools layout panel, showing our grid columns and rows</figcaption>
</figure>
<p>Although I started placing items by line number on the row axis, this quickly became difficult to manage. Items need to overlap each other, and I found it difficult to keep track of where one item should end and another should begin. Additionally, I didn’t want to use negative grid lines because there’s a good chance I might want to add to my layout in the future. If I ended up adding more explicit rows to the grid, then the negative line numbers would no longer be correct, potentially causing a lot of layout bugs!</p>
<p>This was when I decided to create named grid areas on the row axis. Grid areas are created in two ways:</p>
<ol>
<li>With the <code>grid-template-areas</code> property, which enables you to effectively “draw” your grid layout as ascii art.</li>
<li>Using named grid lines, by using <code>-start</code> and <code>-end</code> as suffixes to our line names.</li>
</ol>
<p>The <code>grid-template-areas</code> property doesn’t allow us to define areas for overlapping items, so it doesn’t really help us with this particular layout. Using named grid areas will definitely make life easier, however.</p>
<p>If we name lines on both the row and the column axis, we get a grid area (<em>fig 8</em>):</p>
<figure>
<img src="https://css-irl.info/building-a-scrapbook-layout_09.png" alt="Names grid area defined by grid lines" />
<figcaption><em>Fig 8</em> Suffixing line names with -start and -end creates a grid area</figcaption>
</figure>
<p>It’s then possible to reference that area when placing an item using the <code>grid-area</code> property:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.item</span> <span class="token punctuation">{</span>
<span class="token property">grid-area</span><span class="token punctuation">:</span> image<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>This makes our code more concise and easier to read than using <code>grid-column</code> and <code>grid-row</code>, and writing out the line names longhand:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.item</span> <span class="token punctuation">{</span>
<span class="token property">grid-row</span><span class="token punctuation">:</span> image-start / image-end<span class="token punctuation">;</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> image-start / image-end<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>But we only need the named grid area on the row axis this time. That’s fine, as we can just reference it with the <code>grid-row</code> property:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.item</span> <span class="token punctuation">{</span>
<span class="token property">grid-row</span><span class="token punctuation">:</span> image<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>As we have a large number of rows, I find it much clearer to write our <code>grid-template-rows</code> property vertically, so that it mirrors the structure of the page:</p>
<pre class="language-css"><code class="language-css"><span class="token property">grid-template-rows</span><span class="token punctuation">:</span>
<span class="token comment">/* The first two rows deal with the header */</span>
auto
3rem
<span class="token comment">/* The rest of the grid content starts here */</span>
<span class="token function">minmax</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--verticalPadding<span class="token punctuation">)</span><span class="token punctuation">,</span> auto<span class="token punctuation">)</span>
<span class="token function">minmax</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> auto<span class="token punctuation">)</span>
<span class="token function">minmax</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--verticalPadding<span class="token punctuation">)</span><span class="token punctuation">,</span> auto<span class="token punctuation">)</span>
<span class="token function">var</span><span class="token punctuation">(</span>--overlap<span class="token punctuation">)</span>
<span class="token function">minmax</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--verticalPadding<span class="token punctuation">)</span><span class="token punctuation">,</span> auto<span class="token punctuation">)</span>
<span class="token function">minmax</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> auto<span class="token punctuation">)</span>
<span class="token function">minmax</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--verticalPadding<span class="token punctuation">)</span><span class="token punctuation">,</span> auto<span class="token punctuation">)</span>
<span class="token function">var</span><span class="token punctuation">(</span>--overlap<span class="token punctuation">)</span>
<span class="token function">minmax</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--verticalPadding<span class="token punctuation">)</span><span class="token punctuation">,</span> auto<span class="token punctuation">)</span>
<span class="token function">minmax</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> auto<span class="token punctuation">)</span>
<span class="token function">minmax</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--verticalPadding<span class="token punctuation">)</span><span class="token punctuation">,</span> auto<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre>
<p>Now adding the line names in the correct place becomes simpler and less error-prone, as we can visualise the structure of the grid:</p>
<pre class="language-css"><code class="language-css"><span class="token property">grid-template-rows</span><span class="token punctuation">:</span>
[header-start]
auto
[fig1-start]
3rem
[header-end]
<span class="token function">minmax</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--verticalPadding<span class="token punctuation">)</span><span class="token punctuation">,</span> auto<span class="token punctuation">)</span>
[p1-start]
<span class="token function">minmax</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> auto<span class="token punctuation">)</span>
[p1-end]
<span class="token function">minmax</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--verticalPadding<span class="token punctuation">)</span><span class="token punctuation">,</span> auto<span class="token punctuation">)</span>
[fig2-start]
<span class="token function">var</span><span class="token punctuation">(</span>--overlap<span class="token punctuation">)</span>
[fig1-end]
<span class="token function">minmax</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--verticalPadding<span class="token punctuation">)</span><span class="token punctuation">,</span> auto<span class="token punctuation">)</span>
[p2-start]
<span class="token function">minmax</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> auto<span class="token punctuation">)</span>
[p2-end]
<span class="token function">minmax</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--verticalPadding<span class="token punctuation">)</span><span class="token punctuation">,</span> auto<span class="token punctuation">)</span>
[fig3-start]
<span class="token function">var</span><span class="token punctuation">(</span>--overlap<span class="token punctuation">)</span>
[fig2-end]
<span class="token function">minmax</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--verticalPadding<span class="token punctuation">)</span><span class="token punctuation">,</span> auto<span class="token punctuation">)</span>
[p3-start]
<span class="token function">minmax</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> auto<span class="token punctuation">)</span>
[p3-end]
<span class="token function">minmax</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--verticalPadding<span class="token punctuation">)</span><span class="token punctuation">,</span> auto<span class="token punctuation">)</span>
[fig3-end]<span class="token punctuation">;</span></code></pre>
<p>All that remains is to reference the area names on the row axis when placing our grid items:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.fig--1</span> <span class="token punctuation">{</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> span 5 / -1<span class="token punctuation">;</span>
<span class="token property">grid-row</span><span class="token punctuation">:</span> fig1<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.fig--2</span> <span class="token punctuation">{</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> 1 / span 7<span class="token punctuation">;</span>
<span class="token property">grid-row</span><span class="token punctuation">:</span> fig2<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.fig--3</span> <span class="token punctuation">{</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> span 5 / -2<span class="token punctuation">;</span>
<span class="token property">grid-row</span><span class="token punctuation">:</span> fig3<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>The finished result (<em>fig 10</em>) is available to explore on <a href="https://codepen.io/michellebarker/pen/gOYqmJQ">Codepen</a>.</p>
<figure>
<img src="https://css-irl.info/building-a-scrapbook-layout_10.jpg" alt="Full page screenshot of the finished layout" />
<figcaption><em>Fig 10</em></figcaption>
</figure>
<iframe height="407" style="width: 100%;" scrolling="no" title="Recreating my child’s pre-school scrapbook with CSS Grid" src="https://codepen.io/michellebarker/embed/gOYqmJQ?height=407&theme-id=0&default-tab=css" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/gOYqmJQ">Recreating my child’s pre-school scrapbook with CSS Grid</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>Although I haven’t gone to the extra effort of making this layout responsive, it is useable down to smaller tablet-sized screens. Adjusting the layout for small screen sizes would now be relatively straighforward. At smaller screen sizes I would personally opt for a simpler grid, as we would likely lose a lot of the visual nuance the coumpound grid. But once again, there’s no right or wrong – and even those of us who regularly work with CSS Grid are still figuring out as we go along!</p>
Thoughts on the State of the Web2019-09-16T00:00:00Zhttps://css-irl.info/thoughts-on-the-state-of-the-web/<figure>
<img src="https://css-irl.info/thoughts-on-the-state-of-the-web.jpg" alt="Laura Kalbag on stage with a slide that says “We can’t know from these data if Bootstrap introduced these errors, but there is a strong correspondence of increased errors when Bootstrap is present“" />
<figcaption>Laura Kalbag on stage at State of the Browser 2019</figcaption>
</figure>
<p>I’ve just come back from <a href="https://2019.stateofthebrowser.com/">State of the Browser</a>, a wonderful, community-run conference in London, where I gave a talk about debugging CSS Grid. The conference intentionally focused on web standards, rather than the latest tooling or frameworks. Each of the speakers had their own area of expertise, but what was especially successful was the way the talks were, whether by accident or design, woven seemlessly together by a common thread: making the web work for everyone. <a href="https://www.brucelawson.co.uk/">Bruce Lawson</a> set the tone early on by quoting Sir Tim Berners Lee (or “Uncle Timbo”, as Bruce would have him known):</p>
<blockquote>
<p>This is for everyone.</p>
</blockquote>
<p>It has certainly struck me (and no doubt many others) in recent times that the value of a front end developer seems to be the JavaScript they can write and the fancy frameworks they can use, leaving web fundamentals like CSS, HTML and accessibility worryingly undervalued. Whole swathes of the web are entirely inaccessible to many people with any kind of impairment, in developing countries, or without access to the latest device or a fast connection. We <em>haven’t</em> truly built a web for everyone.</p>
<p>It would be difficult to single out any particular talk from State of the Browser as a highlight, as the standard was so high across the board. HTML, accessibility, performance and CSS were all covered. What was particularly notable was that JavaScript was barely mentioned, except with a warning to use it sparingly. This might feel old-fashioned, but in fact it emphasised how little JavaScript is actually vital to the user experience, and how important it is to invest in the skills that make the web useable for everyone. Often, we’re sacrificing accessibility for the sake of a slick experience for the minority or, even worse, for the sake of developer experience. <a href="https://laurakalbag.com/">Laura Kalbag</a> pointed out in her talk that there is a correlation between use of frameworks and higher instances of accessibility errors. There could be many different reasons for that – it doesn’t necessarily follow that frameworks <em>cause</em> the errors – but when the emphasis is on learning a framework, rather than fully understanding the fundamentals, then it certainly seems logical that accessibility suffers as a result.</p>
<p>Mostly, JavaScript is an enhancement, and yet often it’s a skill prized above all else to companies looking to hire developers. Most job adverts these days highlight JavaScript as the number one must-have, with HTML and CSS thrown in for good measure (if at all), as if they can be picked up in an afternoon, and don’t require years of practical experience and careful consideration to do well. Accessibility is a large and complex area, yet it’s treated as an afterthought, with the people who specialise in it hugely undervalued.</p>
<p>I’d like to think that conferences like State of the Browser are symptomatic of a renewed industry-wide focus on web fundamentals. But part of me fears this isn’t the case, and that it further demonstrates the divide between framework-focused developers and those concerned with the web’s founding principles.</p>
<p>There doesn’t need to be this divide.</p>
<p>I would never discourage someone entering the industry from learning a JS framework because, for better or worse, it’s a skill that <em>might get you a job</em>. It’s all very well for established and well-connected people in the industry to denigrate frameworks, but there’s no getting around the fact that companies <em>do</em> want those skills. I worry that some well-meaning advice could cause newer developers to exclude themselves from the talent pool by eschewing frameworks, and never get onto the career ladder. But above anything else, State of the Browser reminded me how necessary it is to keep banging the drum for a better and more inclusive web, as we can’t afford to let the message get lost. I hope the developers entering this brave new JavaScript world attend conferences like this, listen to the advice, and in turn become advocates of a web for everyone.</p>
<h2>Further reading</h2>
<p>This article by <a href="https://bryanlrobinson.com/">Bryan Robinson</a> is worth reading: <a href="https://blog.logrocket.com/what-the-rule-of-least-power-means-for-modern-developers-b846010a8595/">What the Rule of Least Power means for modern developers</a>. It refers to <a href="https://www.w3.org/2001/tag/doc/leastPower.html">The Rule of Least Power</a>, a principle drawn up by Sir Tim Berners-Lee and Noah Mendelsohn, which recommends choosing the least powerful language for a given purpose – also referenced in Bruce Lawson’s talk.</p>
Get ready for the CSS Grid Revolution2019-09-06T00:00:00Zhttps://css-irl.info/get-ready-for-the-css-grid-revolution/<p>An interview for Net magazine</p>
How to Accessibly Split Text2019-08-27T00:00:00Zhttps://css-irl.info/how-to-accessibly-split-text/<p>I recently published an article on <a href="https://css-irl.info/variable-font-animation-with-css-and-splitting-js">animating variable fonts</a> with the help of the Javascript library <a href="https://splitting.js.org/">Splitting.js</a>. A few people asked about the accessibility implications of this, so in this article we’ll take a look at why splitting a string of text can be problematic from an accessibility point of view, and what we can do to make sure that split text is accessible to everyone.</p>
<h2>Splitting.js recap</h2>
<p>Let’s say you have a word, a heading, a paragraph or a sentence and you want to change the style on a per-letter basis. The way that <em>Splitting.js</em> works is it wraps each character (including whitespace characters) in a <code><span></code> tag and adding various attributes that allow you to more easily target and manipulate each one. It also wraps each word in its own span, so you can target them individually too. There are many creative possibilities!</p>
<p>The following Codepen demo contains an example of <em>Splitting.js</em> in action, using custom properties in CSS to calculate a colour value for each letter. In this article I’ll describe the techniques I used to make it accessible.</p>
<iframe height="369" style="width: 100%;" scrolling="no" title="Splitting JS accessible text with ARIA attributes" src="https://codepen.io/michellebarker/embed/qBWrzLL/?height=369&theme-id=0&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/qBWrzLL/">Splitting JS accessible text with ARIA attributes</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<h2>Why is splitting text an accessibility concern?</h2>
<p>Some people who are blind, partially-sighted, or find reading on the web difficult or problematic for different reasons might use screen reader software to assist them in navigating and exploring a website. Screen readers announce the content of the webpage aurally to a user. To better understand the experience of a person using a screen reader, I recommend watching <a href="https://www.smashingmagazine.com/2019/02/accessibility-webinar/">How A Screen Reader User Accesses The Web</a>, an accessibility webinar from Smashing Magazine.</p>
<p>This is one reason why semantic HTML is especially important: not everyone accesses our webpages visually, so using the right HTML elements for the right purpose makes navigating the page and finding relevant content much easier.</p>
<p>We might want to split a string of text for presentation purposes, but changing the markup within (for example) a heading can affect how screenreaders interpret the text and read it back to the user. Consider the following markup – a simple <code><h1></code> heading tag:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>Oh hello there<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></code></pre>
<p>Now let’s look at the same heading split into <code>span</code>s:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>O<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>h<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>H<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>e<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>l<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>l<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>o<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>T<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>h<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>e<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>r<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>e<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></code></pre>
<p>With each character wrapped in an individual tag, some screenreaders will not interpret each word, but instead announce each letter individually. This would not be a very helpful experience for someone navigating the page using a screenreader!</p>
<p>This behaviour is not consistent between screenreaders. I initially tested this with VoiceOver on Safari, which has no problems reading the text as intended. Others, however, omit the word breaks and read the content as a single long word.</p>
<h2>Making it accessible with WAI-ARIA</h2>
<p>Luckily, these accessibility concerns <em>don’t</em> mean that we can’t use cool libraries like Splitting.js. We just need to go to a tiny bit more effort to ensure our text is accessible to everyone.</p>
<p><a href="https://www.w3.org/WAI/standards-guidelines/aria/">WAI-ARIA</a> provides us with attributes for defining how elements should be presented to assistive technologies. While it is designed to help make websites more accessible, it is not a substitute for semantic HTML. It should be used when semantic HTML alone is not enough.</p>
<h3>aria-label</h3>
<p>In the case of our example heading, we can provide an accessible text label for screen readers with the <code>aria-label</code> attribute:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Oh hello there<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>O<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>h<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>H<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>e<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>l<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>l<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>o<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>T<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>h<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>e<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>r<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>e<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></code></pre>
<p>Using <code>aria-label</code> alone can cause some screen readers to read out the text to read out both the text label <em>and</em> the content. This is far from ideal – we don’t want screen reader users to have to listen to the text being spelt out for them after hearing the label. so we need to hide the element’s inner content from screen readers, which we can do using <code>aria-hidden</code>.</p>
<h3>aria-hidden</h3>
<p><code>aria-hidden</code> hides the element from the accessibility tree, so a screen reader will ignore it. We can’t hide the element itself, as then it won’t be read at all – but we can hide its children. So we have a choice here: we could add <code>aria-hidden</code> to each <code><span></code> in our heading:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Oh hello there<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">aria-hidden</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>O<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">aria-hidden</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>h<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">aria-hidden</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">aria-hidden</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>H<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">aria-hidden</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>e<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">aria-hidden</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>l<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
...
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></code></pre>
<p>Or, if this feels a little tedious, we might choose to group all the children inside another span, and add <code>aria-hidden</code> to that instead:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Oh hello there<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">aria-hidden</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>O<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>h<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>H<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>e<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>l<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>l<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>o<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>T<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>h<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>e<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>r<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>e<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></code></pre>
<h3>Using Javascript to add ARIA attributes</h3>
<p>If we’re using <em>Spitting.js</em> to create those child elements, we can add <code>aria-hidden="true"</code> to each word by using a <code>forEach</code> loop. As I mentioned earlier, <em>Splitting</em> splits a sentence into words and wraps each one in a <code><span></code>, as well as wrapping each character. <code>Splitting()</code> returns an array of target elements, so we firstly need to loop over each one, then loop over each word within the split element. Then we can check if the element has an <code>aria-label</code> attribute, and if it does we add <code>aria-hidden</code>:</p>
<pre class="language-js"><code class="language-js"><span class="token comment">/* Loop through all split elements */</span>
<span class="token function">Splitting</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">s</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token comment">/* Loop through words */</span>
s<span class="token punctuation">.</span>words<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token parameter">word</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
<span class="token comment">/* If the parent element includes `aria-label`, set `aria-hidden="true"` */</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>word<span class="token punctuation">.</span>parentElement<span class="token punctuation">.</span><span class="token function">getAttribute</span><span class="token punctuation">(</span><span class="token string">'aria-label'</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
word<span class="token punctuation">.</span><span class="token function">setAttribute</span><span class="token punctuation">(</span><span class="token string">'aria-hidden'</span><span class="token punctuation">,</span> <span class="token boolean">true</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span></code></pre>
<p>This will result in an HTML structure something like this:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span> <span class="token attr-name">aria-label</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Oh hello there<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">aria-hidden</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>O<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>h<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">aria-hidden</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>H<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>e<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>l<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>l<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>o<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">aria-hidden</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>true<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>T<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>h<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>e<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>r<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>e<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></code></pre>
<p>The user only hears the contents of the <code>aria-label</code> attribute, not the text inside the element itself. That takes care of our accessibility concerns and means we can split the text content of the element safely, knowing that it will be accessible to all.</p>
<p>It would be great if <em>Splitting.js</em> could do this by default, although there are a lot of different considerations to take into account for different types of text. There is currently an <a href="https://github.com/shshaw/Splitting/issues/19">open Github issue</a> for adding accessibility features.</p>
<p><em>Thanks to <a href="https://andy-bell.design/">Andy Bell</a> for signposting this accessibility solution in my twitter feed after I published the original post!</em></p>
Variable Font Animation with CSS and Splitting JS2019-08-13T00:00:00Zhttps://css-irl.info/variable-font-animation-with-css-and-splitting-js/<p>A little while ago I made an animated variable font demo on <a href="https://codepen.io/michellebarker/pen/bPEWGK">Codepen</a>. In this article I’ll explain what variable fonts are, how they can be animated with CSS, and how I created a breathing effect with CSS and a little bit of Javascript.</p>
<iframe height="391" style="width: 100%;" scrolling="no" title="Variable font animation" src="https://codepen.io/michellebarker/embed/bPEWGK/?height=391&theme-id=0&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/bPEWGK/">Variable font animation</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<h2>Introducing variable fonts</h2>
<p>Variable fonts are exciting new development in web typography. Instead of multiple font files to load different variants of a particular font, variable fonts allow us to load all of the variations from a single file. In most cases this is a big performance win (although the file tends to be larger than a regular font file on its own, so it’s best to only use a variable font if you actually need it).</p>
<h3>One font, many variations</h3>
<p>Instead of a handful of font weights that are only available in multiples of 100 (e.g. <code>font-weight: 600</code>), variable fonts provide a range of values, all from a single file. The weight can be varied anywhere within that range. So <code>font-weight: 372</code> is perfectly valid!</p>
<h3>Axes of variation</h3>
<p>Weight is just one of the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide#Introducing_the_'variation_axis'">axes of variation</a> (although probably the most common one). Variable fonts can come with different axes too. There are a number of <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide#Registered_axes_and_existing_CSS_attributes">registered axes</a>, which correspond to a four-letter tag:</p>
<ul>
<li>weight (<code>wght</code>)</li>
<li>width (<code>wdth</code>)</li>
<li>italic (<code>ital</code>)</li>
<li>slant (<code>slnt</code>)</li>
<li>optical size (<code>opsz</code>)</li>
</ul>
<p>These correspond to CSS properties and values:</p>
<ul>
<li><code>font-weight</code></li>
<li><code>font-stretch</code></li>
<li><code>font-style</code></li>
<li><code>font-style</code></li>
<li><code>font-optical-sizing</code></li>
</ul>
<p>Not all variable fonts contain all of these axes of variation. Many contain just one or two.</p>
<p>They can also be accessed using the <code>font-variation-settings</code> property. This property enables us to not only adjust the standard axes, but custom axes as well. So <code>font-weight</code> could be specified in two ways:</p>
<pre><code>font-weight: 372;
</code></pre>
<p>or</p>
<pre><code>font-variation-settings: 'wght' 372;
</code></pre>
<aside>We could use `font-weight` to provide a fallback for browsers that do not support variable fonts.</aside>
<h2>Custom axes</h2>
<p>Custom axes provide the type designer with infinite scope for creativity! A custom axis of variation could be literally anything – some, like <em>x-height</em>, might be fairly common for a typeface, but there are many more creative possibilities.</p>
<p>Custom axes can be accessed with the <code>font-variation-settings</code> property but, unline standard axes, their four-letter tag name must be uppercase. The variable font <a href="http://www.nmtype.com/movement">Movement</a> by NM Type provides a custom axis called <em>space</em>, which controls the curvture of the letterforms.</p>
<pre><code>font-variation-settings: 'wght' 200, 'SPAC' 118;
</code></pre>
<p>Try playing around with the different axes in this demo:</p>
<iframe height="360" style="width: 100%;" scrolling="no" title="Movement variable font" src="https://codepen.io/michellebarker/embed/xxKGyvd/?height=360&theme-id=0&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/xxKGyvd/">Movement variable font</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<h2>Animating a variable font with CSS</h2>
<p><code>font-variation-settings</code> is animatable, and because it covers a range of values rather than increments of 100, we can get some really nice effects with simple CSS transitions or keyframe animations. The font <em>IBM Plex Sans</em> has two axes of variation: weight and width. The following code sets a 1s looping animation of both axes:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">h1</span> <span class="token punctuation">{</span>
<span class="token property">font-variation-settings</span><span class="token punctuation">:</span> <span class="token string">'wght'</span> 100<span class="token punctuation">,</span> <span class="token string">'wdth'</span> 85<span class="token punctuation">;</span>
<span class="token property">animation</span><span class="token punctuation">:</span> breathe 4000ms infinite forwards<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token atrule"><span class="token rule">@keyframes</span> breathe</span> <span class="token punctuation">{</span>
<span class="token selector">60%</span> <span class="token punctuation">{</span>
<span class="token property">font-variation-settings</span><span class="token punctuation">:</span> <span class="token string">'wght'</span> 700<span class="token punctuation">,</span> <span class="token string">'wdth'</span> 100<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">100%</span> <span class="token punctuation">{</span>
<span class="token property">font-variation-settings</span><span class="token punctuation">:</span> <span class="token string">'wght'</span> 100<span class="token punctuation">,</span> <span class="token string">'wdth'</span> 85<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>This gives the effect of our text breathing in and out!</p>
<iframe height="370" style="width: 100%;" scrolling="no" title="Variable font animation" src="https://codepen.io/michellebarker/embed/rExyEE/?height=370&theme-id=0&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/rExyEE/">Variable font animation</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>Alternatively, this could be a nice hover effect with a transition instead of an animation.</p>
<h2>Staggering the animation</h2>
<p>Instead of our entire text animating at the same rate, it might be nice to have our letterforms animate in sequence. We <em>could</em> wrap each letter of our text in a <code><span></code> and set a <code>animation-delay</code> on each one:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>B<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>r<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>e<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>a<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>t<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>h<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>i<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>n<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span><span class="token punctuation">></span></span>g<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></code></pre>
<pre class="language-css"><code class="language-css"><span class="token selector">h1 span:nth-child(2)</span> <span class="token punctuation">{</span>
<span class="token property">animation-delay</span><span class="token punctuation">:</span> 400ms<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">h1 span:nth-child(3)</span> <span class="token punctuation">{</span>
<span class="token property">animation-delay</span><span class="token punctuation">:</span> 800ms<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">h1 span:nth-child(4)</span> <span class="token punctuation">{</span>
<span class="token property">animation-delay</span><span class="token punctuation">:</span> 1200ms<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">/* etc...*/</span></code></pre>
<p>This would be a bit laborious to write (although we could use Sass to help us), and it wouldn’t be very maintainable if we decided to change our text content at a later date.</p>
<p>But if we don’t mind using just a little bit of Javascript, there’s a great library called <a href="https://splitting.js.org/">Splitting.js</a> that is perfect for this!</p>
<h2>Splitting</h2>
<p>Splitting’s primary use case is for animating text, although it’s also possible to split grids and layouts for some cool effects. To use it we need to include the library in our project, then set a <code>data-splitting</code> attribute on the element we’d like to animate:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h1</span> <span class="token attr-name">data-splitting</span><span class="token punctuation">></span></span>Breathing<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h1</span><span class="token punctuation">></span></span></code></pre>
<p>Now the JS we need to write is very simple:</p>
<pre class="language-js"><code class="language-js"><span class="token function">Splitting</span><span class="token punctuation">(</span><span class="token punctuation">)</span></code></pre>
<p>Splitting then splits our text element into a series of <code><span></code>s, each with a class, a data-attribute and a custom property definition with a value of the character index, which we can then access in our CSS:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>word<span class="token punctuation">"</span></span> <span class="token attr-name">data-word</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>Breathing<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">--word-index</span><span class="token punctuation">:</span>0<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>char<span class="token punctuation">"</span></span> <span class="token attr-name">data-char</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>B<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">--char-index</span><span class="token punctuation">:</span>0<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>B<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>char<span class="token punctuation">"</span></span> <span class="token attr-name">data-char</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>r<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">--char-index</span><span class="token punctuation">:</span>1<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>r<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>char<span class="token punctuation">"</span></span> <span class="token attr-name">data-char</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>e<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">--char-index</span><span class="token punctuation">:</span>2<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>e<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>char<span class="token punctuation">"</span></span> <span class="token attr-name">data-char</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>a<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">--char-index</span><span class="token punctuation">:</span>3<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>a<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>char<span class="token punctuation">"</span></span> <span class="token attr-name">data-char</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>t<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">--char-index</span><span class="token punctuation">:</span>4<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>t<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>char<span class="token punctuation">"</span></span> <span class="token attr-name">data-char</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>h<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">--char-index</span><span class="token punctuation">:</span>5<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>h<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>char<span class="token punctuation">"</span></span> <span class="token attr-name">data-char</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>i<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">--char-index</span><span class="token punctuation">:</span>6<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>i<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>char<span class="token punctuation">"</span></span> <span class="token attr-name">data-char</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>n<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">--char-index</span><span class="token punctuation">:</span>7<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>n<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>span</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>char<span class="token punctuation">"</span></span> <span class="token attr-name">data-char</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>g<span class="token punctuation">"</span></span> <span class="token special-attr"><span class="token attr-name">style</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span><span class="token value css language-css"><span class="token property">--char-index</span><span class="token punctuation">:</span>8<span class="token punctuation">;</span></span><span class="token punctuation">"</span></span></span><span class="token punctuation">></span></span>g<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>span</span><span class="token punctuation">></span></span></code></pre>
<p>In order to create a sequential animation, we can use <code>calc()</code> to calculate the <code>animation-delay</code> value for each letter from the custom property:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">h1 .char</span> <span class="token punctuation">{</span>
<span class="token property">--delay</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token function">var</span><span class="token punctuation">(</span>--char-index<span class="token punctuation">)</span> + 1<span class="token punctuation">)</span> * 400ms<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">animation</span><span class="token punctuation">:</span> breathe 4000ms infinite both<span class="token punctuation">;</span>
<span class="token property">animation-delay</span><span class="token punctuation">:</span> <span class="token function">var</span><span class="token punctuation">(</span>--delay<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>That massively cuts down on the CSS we need to write, and means that we could change the text later on and still have our animation work perfectly!</p>
<h2>Resources</h2>
<h3><a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide#Variable_Fonts_what_they_are_and_how_they_differ">MDN’s Variable Fonts Guide</a></h3>
<p>MDN’s guide is a great resource for learning about variable fonts and how to use them</p>
<h3><a href="https://v-fonts.com/">V-Fonts</a></h3>
<p>V-Fonts is a listing of hundreds of variable fonts, including their variations axes and where to find them. It includes a mixture of paid and free/open source fonts, and is a great place to find samples for using in demos if you don’t want to fork out big bucks just yet.</p>
<h3><a href="https://www.axis-praxis.org/">Axis-Praxis</a></h3>
<p>Axis-Praxis is a playground for experimenting with variable fonts and understanding some of the creative possibilities. It includes some really interesting and creative samples!</p>
<h3><a href="https://variablefonts.dev/">VariableFonts.dev</a></h3>
<p>Variablefonts.dev is a project by <a href="https://twitter.com/mandy_kerr">Mandy Michael</a>, who is well-known in the CSS world for creating awe-inspiring demos with variable fonts and speaking about them all over the world.</p>
Heatwave: An Animated CSS Sun Illustration2019-07-26T00:00:00Zhttps://css-irl.info/heatwave-animated-sun-illustration/<figure>
<video width="100%" controls="" playsinline="">
<source src="https://css-irl.info/media/heatwave-animated-sun-illustration.mp4" type="video/mp4" />
</video>
<figcaption>Animated sun illustration</figcaption>
</figure>
<p>We’re right in the middle of a heatwave here in the UK, and things have been a little quiet on this blog while I’ve been very busy with various other projects. So I’d thought I’d take a little break today and try something a little bit fun – an animated CSS sun illustration, to capture the summer spirit (and the relentless heat)!</p>
<p>Initially I started layering <code>div</code>s, but then I realised I could create the whole thing with just a single element, using background gradients! The first (static) version was quite simple – I didn’t even need any pseudo elements (<em>fig 01</em>).</p>
<figure>
<img src="https://css-irl.info/heatwave-animated-sun-illustration.png" alt="Simple sun illustration" />
<figcaption><span>Fig 01</span> The first iteration</figcaption>
</figure>
<p>But it felt quite basic. I wanted to give the sun’s rays a bit more life, and add some animation. So I added two pseudo elements (<code>::before</code> and <code>::after</code>).</p>
<h2>Layered gradients</h2>
<p>The two pseudo elements, and the element itself, all have mutliple layered background gradients. These use <code>radial-gradient</code> for the glow, and <code>conic-gradient</code> for the rays. <code>conic-gradient</code> has mixed browser support at the time of writing, so the demo can only be viewed in Chrome or Safari – you won’t see anything in Firefox. Although Firefox supports <code>radial-gradient</code>, because we’re using it in combination with <code>conic-gradient</code> the whole declaration is dropped.</p>
<p>I gave the main body of the sun a slightly fuzzy edge by adding a few pixels between the orange colour stop and the transparent region – I didn’t want it to feel too sharp and solid:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.sun::after</span> <span class="token punctuation">{</span>
<span class="token property">background</span><span class="token punctuation">:</span> <span class="token function">radial-gradient</span><span class="token punctuation">(</span>
yellow<span class="token punctuation">,</span>
orange 27%<span class="token punctuation">,</span>
transparent <span class="token function">calc</span><span class="token punctuation">(</span>27% + 3px<span class="token punctuation">)</span> 100%
<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h3>Z-index and backgrounds</h3>
<p>I ran into a bit of a <code>z-index</code> issue when layering my gradients – ideally I wanted the body of the sun to be on the main element and the pseudo elements to only contain the sun’s rays, so that I could animate them independently. But I couldn’t get the z-indexes to play nicely. It seems the background of the element is always going to be behind the pseudo elements, no matter what, which kind of makes sense – although if this is incorrect please let me know!</p>
<p>I could have easily got around this by nesting an element, but I wanted to keep the purity of the single-element solution! In the end I decided not to scale the second set of rays anyway, but having the option would be useful (without resorting to anything too complex).</p>
<h2>Mask-image</h2>
<p>To give the rays a soft edge I used <code>mask-image</code> with a radial gradient over the element. <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/mask">Image masking</a> is similar to <code>clip-path</code> in that it is a way to show or hide part of an element. But instead of providing a path to cut out the shape, it’s more like drawing over the areas you want to show. You can use a PNG, SVG or gradient to mask, for example.</p>
<p><code>mask-image</code> requires a prefix for some browsers (including Chrome):</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.sun</span> <span class="token punctuation">{</span>
<span class="token property">-webkit-mask-image</span><span class="token punctuation">:</span> <span class="token function">radial-gradient</span><span class="token punctuation">(</span><span class="token function">rgba</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 1<span class="token punctuation">)</span> 40%<span class="token punctuation">,</span> transparent 65%<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">mask-image</span><span class="token punctuation">:</span> <span class="token function">radial-gradient</span><span class="token punctuation">(</span><span class="token function">rgba</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 0<span class="token punctuation">,</span> 1<span class="token punctuation">)</span> 40%<span class="token punctuation">,</span> transparent 65%<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Because I’m animating the scale of the rays I duplicated this property onto the pseudo element too, so that the same proportion would remain hidden as it scales.</p>
<h2>Animation</h2>
<p>I animated the two pseudo elements so they rotated at different rates. This wasn’t quite enough, so I animated the scale of one of them too, to give the impression of a pulsating glow.</p>
<p>Here’s the full demo:</p>
<iframe height="449" style="width: 100%;" scrolling="no" title="Animated sun (best viewed in Chrome)" src="https://codepen.io/michellebarker/embed/QeKEVp/?height=449&theme-id=0&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/QeKEVp/">Animated sun (best viewed in Chrome)</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
Part 3: Auto-flow, Order and Item Placement2019-07-08T00:00:00Zhttps://css-irl.info/debugging-css-grid-part-3-auto-flow-order-and-item-placement/<p>When it comes to building a layout, CSS Grid offers us a number of different choices for placing items. In this article we’ll take a look at the different placement methods, how to use auto-flow to avoid having to explicitly place every item, and why sometimes grid items might not be placed where you expect them to be.</p>
<p>I like to think of using Grid as two distinct parts:</p>
<ol>
<li>Scaffolding – or building the structure of the grid itself. This involves defining the properties acting on the grid container, beginning with <code>display: grid</code>, and describing the shape of the grid and how it should behave using any combination of <code>grid-template-columns</code>, <code>grid-template-rows</code>, <code>grid-auto-columns</code> and <code>grid-auto-rows</code> (or <code>grid</code> / <code>grid-template</code> if using the shorthand).</li>
<li>Furnishing the grid – telling the browser where each child of our grid container should be placed.</li>
</ol>
<p>In this article we’ll take a deeper look at the second part, and in particular auto placement.</p>
<h2>Different ways to place a grid item</h2>
<h3>Explicit placement</h3>
<p>The properties that allow us to explicitly place items on a grid are:</p>
<ul>
<li><code>grid-column-start</code></li>
<li><code>grid-column-end</code></li>
<li><code>grid-row-start</code></li>
<li><code>grid-row-end</code></li>
<li><code>grid-column</code> (shorthand for <code>grid-column-start</code> / <code>grid-column-end</code>)</li>
<li><code>grid-row</code> (shorthand for <code>grid-row-start</code> / <code>grid-row-end</code>)</li>
<li><code>grid-area</code> (shorthand for <code>grid-row-start</code> / <code>grid-column-start</code> / <code>grid-row-end</code> / <code>grid-column-end</code>)</li>
</ul>
<p>The above properties are defined on the <em>items</em> themselves. Possible values include line numbers, line names, span values or area names. There are no rules for which you should and shouldn’t use, and you can mix and match as you like. For example:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.item--a</span> <span class="token punctuation">{</span>
<span class="token property">grid-area</span><span class="token punctuation">:</span> a<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.item--b</span> <span class="token punctuation">{</span>
<span class="token property">grid-area</span><span class="token punctuation">:</span> 2 / 2 / 4 / 4<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.item--c</span> <span class="token punctuation">{</span>
<span class="token property">grid-row</span><span class="token punctuation">:</span> span 2 / 5<span class="token punctuation">;</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> 1 / 3<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h3>grid-template-areas</h3>
<p>Additionally, we have <code>grid-template-areas</code>. This property it is defined on the <em>grid container</em>. It allows us to define grid areas with names, then reference those areas to place the items:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.grid</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token property">grid-template-columns</span><span class="token punctuation">:</span> <span class="token function">repeat</span><span class="token punctuation">(</span>3<span class="token punctuation">,</span> 1fr<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">grid-template-rows</span><span class="token punctuation">:</span> <span class="token function">repeat</span><span class="token punctuation">(</span>4<span class="token punctuation">,</span> 120px<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">grid-template-areas</span><span class="token punctuation">:</span>
<span class="token string">'a a a'</span>
<span class="token string">'. b b'</span>
<span class="token string">'. b b'</span>
<span class="token string">'. . .'</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.a</span> <span class="token punctuation">{</span>
<span class="token property">grid-area</span><span class="token punctuation">:</span> a<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.b</span> <span class="token punctuation">{</span>
<span class="token property">grid-area</span><span class="token punctuation">:</span> b<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>This is pretty cool, as it means we could change the layout significantly by only altering the <code>grid-template-areas</code> value. We don’t need to add or change any properties on the items themselves. A big win for responsive design!</p>
<p>We might be tempted to place every item explicitly using <code>grid-template-areas</code>. But there’s one drawback: You can’t define overlapping areas. Creating a layout like <em>Fig 01</em> could not be done with <code>grid-template-areas</code> alone.</p>
<figure>
<img src="https://css-irl.info/debugging-css-grid-3-01.png" alt="A grid with three items" />
<figcaption><em>Fig 01</em><span>Items 2 and 3 overlap, so only one can have its area defined by `grid-template-areas`.</span></figcaption>
</figure>
<p>But we <em>could</em> use <code>grid-template-areas</code> <em>in addition</em> to placing items by line name or area.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.a</span> <span class="token punctuation">{</span>
<span class="token property">grid-area</span><span class="token punctuation">:</span> a<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.b</span> <span class="token punctuation">{</span>
<span class="token property">grid-area</span><span class="token punctuation">:</span> b<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.c</span> <span class="token punctuation">{</span>
<span class="token property">grid-area</span><span class="token punctuation">:</span> 3 / 1 / 5 / 3<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h3>Auto placement</h3>
<p>If we don’t explicity place items on our grid, they will be auto-placed. By default each grid items have a span of 1 on both the row and the column axis, so they will each be placed into the next available grid cell. We can use this to our advantage: If we have something like a news feed, we don’t want to have to place each item explicitly, especially if we don’t know how many items there will be.</p>
<figure>
<img src="https://css-irl.info/debugging-css-grid-3-02.png" alt="Auto placement" />
<figcaption><em>Fig 02</em><span>No items are explicity placed, therefore they are all auto placed.</span></figcaption>
</figure>
<h2>Predicting auto placement</h2>
<p>In the above example the rules of auto placement are fairly intuitive: Items are placed along the row (or inline) axis until they fill up the row, and then they’ll wrap onto the next row (creating a new row if there isn’t one defined).</p>
<p>But if we have some items that are explicity placed, and others that are not, them how can we identify the cells the auto placed be placed into?</p>
<p>If I place an item on my grid using <code>grid-column: 2 / span 2</code> I might expect that any auto placed items succeeding that one will be placed <em>after</em> the one I’m placing (<em>Fig 03</em>):</p>
<figure>
<img src="https://css-irl.info/debugging-css-grid-3-03.png" alt="Three grid items with a mixture of explicit and auto placement" />
<figcaption><em>Fig 03</em><span>The blue item is explicitly placed on the row and column axis (using `grid-template-columns` and grid-template-columns`).</span></figcaption>
</figure>
<p>What <em>actually</em> happens with the above code is the succeeding items are placed <em>before</em> the placed item. They are placed into the first available cells, which happen to be the first two in our grid.</p>
<figure>
<img src="https://css-irl.info/debugging-css-grid-3-04.png" alt="Three grid items with a mixture of explicit and auto placement" />
<figcaption><em>Fig 04</em><span>The blue item is explicitly placed only on the column axis (using grid-template-columns`).</span></figcaption>
</figure>
<p>But if we place the blue item only on the column axis, the items <em>are</em> placed after the first one:</p>
<figure>
<img src="https://css-irl.info/debugging-css-grid-3-05.png" alt="Three grid items with a mixture of explicit and auto placement" />
<figcaption><em>Fig 05</em><span>The blue item is placed on the column axis with `grid-column: 2 / 4`.</span></figcaption>
</figure>
<p>So why is the placement behaviour different? If we understand the rules of auto placement, things become clearer.</p>
<h2>Understanding flow</h2>
<p>A good way to think about this is to think of our grid as a flowing river. Any explicitly placed items are boats anchored in the river. Auto placed items flow around these, from left to right (<em>Fig 06</em>).</p>
<figure>
<img src="https://css-irl.info/debugging-css-grid-3-06.png" alt="Grid items with a mixture of explicit and auto placement" />
<figcaption><em>Fig 06</em></figcaption>
</figure>
<p>Grid items that are only explicitly placed on one axis are more loosly anchored. They participate in the grid flow on the remaining axis.</p>
<p>Items placed using a span value alone will still flow like the others, but they’ll be restricted by their own explicit size. An item with a span of 2 will flow onto the next row if there are less than 2 grid columns available. We can think of these as being semi-auto placed (<em>Fig 07</em>).</p>
<figure>
<img src="https://css-irl.info/debugging-css-grid-3-07a.png" alt="Grid items with a mixture of explicit and auto placement" />
<figcaption><em>Fig 07</em> Item 2 has a column span of 3, so will wrap onto the next line.</figcaption>
</figure>
<p>In <em>Fig 07</em> we’re only placing the item on the column axis again (using <code>span</code>), so successive items are placed after it. We have enough items to fill the grid exactly – but rather than filling earlier grid cell, the sixth and seventh items create an implicit track. This <em>doesn’t</em> happen if we only place it explicitly on the row axis (<em>Fig 08</em>).</p>
<figure>
<img src="https://css-irl.info/debugging-css-grid-3-08a.png" alt="Auto placed items filling up the explicit grid" />
<figcaption><em>Fig 08</em><span>Item 2 has a row span of 3</span></figcaption>
</figure>
<p>This is to do with the order in which item placement is resolved in the <a href="https://www.w3.org/TR/css-grid-1/#auto-placement-algo">grid placement algorithm</a>.</p>
<h2>Changing the flow of your grid</h2>
<p>There are two different properties we can use to change the order of items in the grid. The first is <code>grid-auto-flow</code>. This changes the direction of flow, and therefore how items will be auto placed. Possible values are <code>row</code> (default), <code>column</code>, <code>row dense</code> and <code>column dense</code>.</p>
<h3>grid-auto-flow: column</h3>
<p>By changing the value from <code>row</code> to <code>column</code>, we can see in the second section in this demo that the behaviour has now reversed: Items placed on the column axis are now resolved ahead of those on the row axis. Placing an item on the column axis no longer generates an implicit grid track, as items are packed into every cell, but placing an item on the row axis <em>does</em>.</p>
<iframe height="397" style="width: 100%;" scrolling="no" title="Auto flow and distribution" src="https://codepen.io/michellebarker/embed/MMqLdK/?height=397&theme-id=0&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/MMqLdK/">Auto flow and distribution</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<h3>grid-auto-flow: dense</h3>
<p>Using the <code>dense</code> keyword in addition to <code>row</code> or <code>column</code> (<code>row</code> is the default), we can
specify that auto placed items should pack themselves into any available grid cells (including ones preceeding any explicitly placed items). This will ensure there are no gaps, even if an item is placed on only one axis. In the above demo you can see how each of the values for <code>grid-auto-flow</code> alter the behaviour of the grid.</p>
<h3>Order</h3>
<p>The <code>order</code> property is not exclusive to Grid – it can also be used with flexbox. It allows us to change the order of individual items within the flow. Items have a default order of 0. Changing an auto placed item’s order to -1 will place it at the start of the grid auto placed grid items – after explicitly placed items, if there are any placed at the start of the grid.</p>
<p>The <code>order</code> property will only affect auto and semi-auto placed items (those using a single span value). In the demo below the purple item’s position is altered using the <code>order</code> property, but the blue item, which is explicitly placed, is not affected.</p>
<iframe height="447" style="width: 100%;" scrolling="no" title="Order" src="https://codepen.io/michellebarker/embed/vqVJqX/?height=447&theme-id=0&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/vqVJqX/">Order</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<h3>Direction</h3>
<p>You might have noticed, <code>grid-auto-flow</code> allows us to change the direction of flow from <code>row</code> to <code>column</code>, but it does not allow us to make items flow from right to left. Instead, we can use the <code>direction</code> property. This is part of the <a href="https://www.w3.org/TR/css-writing-modes-3/">CSS Writing Modes</a>, which is designed to support international writing modes where text flows in a different direction from left-to-right. To make our grid flow from right to left we can use this:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.grid</span> <span class="token punctuation">{</span>
<span class="token property">direction</span><span class="token punctuation">:</span> rtl<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h3>⚠️ Use with caution ⚠️</h3>
<p>Properties that affect the visual order of grid items should be used with the utmost caution, as the do not change the source order, and can therefore adversely affect accessibility. Rachel Andrew has written extensively about it in her article, <a href="https://rachelandrew.co.uk/archives/2019/06/04/grid-content-re-ordering-and-accessibility">Grid, content re-ordering and accessibility</a>.</p>
<p>I believe there are <em>some</em> cases where visual re-ordering makes sense, but in my experience they most often involve explicit item placement.</p>
<h2>Conclusion</h2>
<p>I hope this article has helped demystify some issues around placement of auto flow grid items. You might also be interested to read part 1 of this series, <a href="https://css-irl.info/debugging-css-grid-part-1-understanding-implicit-tracks/">Understanding Implicit Tracks</a>, which also deals with placement of grid items.</p>
How to Enable Experimental Web Platform Features in Chrome and Firefox2019-06-21T00:00:00Zhttps://css-irl.info/how-to-enable-experimental-web-platform-features/<figure>
<img src="https://css-irl.info/how-to-enable-experimental-features.jpg" alt="Chrome and Firefox logos" />
</figure>
<p>Want to experiment with the latest bleeding-edge web technologies? One way is to download a nightly (or developer) version of the browser. Both Chrome and Firefox have versions of their browsers where they launch experimental or non-standardised features – Chrome Canary and Firefox Nightly respectively. This allows for experimentation and getting feedback before rolling out full support. From time to time, you may need to enable experimental features yourself. This article will show you how to do that in Chrome and Firefox.</p>
<h2>What are experimental features?</h2>
<p>Browsers sometimes hide new features behind flags – meaning they’re not turned on by default for the majority of users. One example is the CSS Grid Layout Level 1 specification, which (when it was first implemented) was behind a flag in Firefox and Chrome. If we look for this in <a href="https://caniuse.com/#search=grid">caniuse.com</a>, we can see that this feature literally has a little flag in earlier versions of these browsers, to denote that it was experimental feature.</p>
<figure>
<img src="https://css-irl.info/how-to-enable-experimental-features-05.png" alt="Screenshot from caniuse.com showing Grid implementation state in different browsers" />
</figure>
<p>While behind a flag, the browser’s implementation of a particular feature may be buggy, and subject to change, but you can still try it out. In fact, if it’s a feature you’re keen to use, the earlier you try it out the better – and if you can give feedback then it’s even more valuable.</p>
<p>Right now, one experimental feature you can try out is subgrid – part of the CSS Grid Level 2 specification. It’s only available in Firefox Nightly, and you’ll need to enable it.</p>
<h2>How to turn on experimental features</h2>
<h3>Firefox</h3>
<ol>
<li>
<p>In your browser, go to the URL <a href="about:config">about:config</a>. This is the “hidden” settings area for your browser, and enables us to do much more than just enable the CSS features we want to work with - there are many other settings too. But be careful, you could break your browser’s functionality if you don’t know what you’re doing!</p>
</li>
<li>
<p>If you haven’t visited this URL before, you’ll get a warning that looks like this:</p>
<figure>
<img src="https://css-irl.info/how-to-enable-experimental-features-02.png" alt="Firefox screenshot" />
</figure>
Click “Accept the risks”.
</li>
<li>
<p>Use the search bar to search for the name of the feature you want to enable. For example, if we search for <code>subgrid</code> we’ll find the following:</p>
<figure>
<img src="https://css-irl.info/how-to-enable-experimental-features-03.png" alt="Firefox screenshot" />
</figure>
<p>The ‘Value’ column on the right determines whether the feature is switched on. Double-click the item to switch the value from ‘false’ to ‘true’. The feature is now enabled.</p>
</li>
</ol>
<h3>Chrome</h3>
<ol>
<li>Go to the URL <a href="https://chrome//flags">chrome://flags</a>. Chrome’s UI is a little more user friendly than Firefox’s here, and it’s quite interesting to read about all the different features that you can enable or disable.</li>
<li>Search for the feature. Searching for CSS here (at the time of writing) only yields one result (CSS fragment identifiers). All features have a dropdown from which you can select the options <em>enabled</em>, <em>disabled</em> or <em>default</em>. <figure>
<img src="https://css-irl.info/how-to-enable-experimental-features-04.png" alt="Chrome screenshot" />
</figure>
The language is somewhat confusing, as it’s not clear whether a ‘Default’ features is enabled or disabled!
</li>
<li>Switch to the desired setting.</li>
</ol>
Video: Building Complex Layouts at Future Sync 20192019-06-15T00:00:00Zhttps://css-irl.info/video-building-complex-layouts-future-sync/<p><a href="https://futuresync.co.uk/">Future Sync</a> conference recently published the videos from their 2019 conference, including the one from my talk, <strong>Building Complex Layouts with CSS Grid</strong>. If you’re new to CSS Grid, or haven’t had the chance to play around with it too much yet, this could be a good introduction. I start of with some basic usage and progressively get onto more complex examples, explaining different placement methods, and using the <code>minmax()</code> function to craft flexible layouts.</p>
<iframe width="560" height="315" src="https://www.youtube.com/embed/-mRaVN5wY3s" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
Pixel Pioneers 2019 Roundup2019-06-10T00:00:00Zhttps://css-irl.info/pixel-pioneers-2019-roundup/<p>Last week I attended <a href="https://pixelpioneers.co/">Pixel Pioneers</a>, an annual conference in my local city of Bristol. It has fast become a firm date in my calendar, and this year didn’t disappoint. In fact, I’d go so far as to say it might be the best one yet.</p>
<p>Due to work commitments I didn’t get to see all the talks, but those that I did see were all exemplary, with lots to learn from each one. Here’s a brief roundup of some key topics and where they sit in today’s web landscape:</p>
<h2>Fix those UX bugs</h2>
<p>A sweary (but enjoyable!) rant by <a href="https://twitter.com/OptimiseOrDie">Craig Sullivan</a>, the first talk, <em>The 15-Minute Model for Optimising Device Experiences</em> took no prisoners. Craig shared some shocking crimes against UX, and his formula for preventing these. Clue – it involves testing on real devices! The key takeaway here was that neglecting testing can result in skewed metrics, frustrated customers, and potential loss of thousands (or even millions) of dollars, especially for big companies. Prioritise these UX bugs over and above fancy new features.</p>
<h2>Exciting things are coming for web animation</h2>
<p><a href="https://lisilinhart.info/">Lis Linhart</a> gave an exceptional talk on web animation, complete with beautiful illustrated slides, and bespoke demos. She took elements from the Pixel Pioneers website to craft examples that really drove home some key points around timing and easing to make animations feel more natural, and showcased some very cool 3D techniques. We learnt about the relative performance impact of animating different CSS properties – transforms and opacity being more performant than, say, widths and margins. She explained how promoting elements to a new layer can improve the performance of animations.</p>
<p>Lis also showed some examples of how you might code more complex animations using the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API">Web Animations API</a> (WAAPI). Unfortunately it’s not supported in any browsers yet, but it looks pretty exciting!</p>
<h2>Demystify flexbox layouts with dev tools</h2>
<p>I was really excited to see <a href="https://www.chenhuijing.com/">Hui-Jing Chen</a>’s talk, <em>Using DevTools to Understand Modern CSS Layouts</em>, and it was worth the wait! She didn’t use any slides – the entire presentation consisted of inspecting flexbox layouts using Firefox’s developer tools. Firefox has a fantastic flexbox inspector, which allows you to see exactly how space is distributed inside your flex container, and the resolved sizes of the flex items. I thought I had a good idea of how flexbox works, but this talk gave me a much better understanding of how <code>flex-basis</code> (one of flexbox’s more mysterious properties!) actually behaves. Viewing this in the browser dev tools suddenly helped it make a lot more sense.</p>
<h2>Subgrid is coming</h2>
<p>As a CSS layout geek, It’s always a treat seeing <a href="https://css-irl.info/pixel-pioneers-2019-roundup/">Rachel Andrew</a> talk about CSS Grid. There’s no one more knowledgable on the subject than Rachel! In this talk she delved into subgrid, a much-requested feature of the CSS Grid Level 2 specfication, which is now available in Firefox Nightly. She showed some use cases that explained really well why we need subgrid – and how current options, like <code>display: contents</code> don’t satisfy all the requirements (as well as leaving gaping holes in accessibility).</p>
<p>Rachel also emphasised how important it is for developers to experiment with, and write about, new CSS features when they aren’t widely supported yet. These experiments and discussions can really shape how the specification develops, and everyone can have a voice in this.</p>
<h2>Design for the many</h2>
<p>In the last talk of the day, designer <a href="http://jontangerine.com/">Jon Tan</a> delivered a powerful rallying cry that really resonated with the audience. Part love-letter to web standards, part call-to-arms, he advocated for a radical approach to design, one with inclusivity at it’s heart. Design for the many, and not the few.</p>
Video: Super-powered Layouts at State of the Browser 20182019-06-08T00:00:00Zhttps://css-irl.info/video-super-powered-layouts-at-state-of-the-browser/<p>Last year I spoke about CSS Grid at <a href="https://2018.stateofthebrowser.com/">State of the Browser</a> conference. This was a great conference to attend, with awesome speakers including <a href="https://adactio.com/">Jeremy Keith</a>, <a href="https://christianheilmann.com/">Christian Heilmann</a>, <a href="https://ruthjohn.com/">Ruth John</a> and <a href="https://www.sonniesedge.net/">Charlie Owen</a>.</p>
<p>The video of my talk, <strong>Super-powered Layouts with CSS Grid and CSS Variables</strong> has just been published. Enjoy!</p>
<p>https://www.youtube.com/embed/hq3hDKD4H98</p>
Top Tips for Hiring Diverse Teams2019-05-29T00:00:00Zhttps://css-irl.info/top-tips-for-hiring-diverse-teams/<figure>
<img src="https://css-irl.info/top-tips-for-hiring-diverse-teams-01.png" alt="A smiling pink heart" />
</figure>
<p>Like many women in tech, I sometimes get asked how companies can recruit a more diverse team. It’s no secret that the tech world is very male-dominated. While I don’t have a magic bullet, I have my own experience of seeking employment in the tech industry, and consequently I have a few opinions on what companies could be doing better in order to attract more diverse applicants. As in so many cases, making something better for a minority of people also has the bonus effect of making it better for <em>everyone</em>. So there’s really no excuse not to be proactive on this.</p>
<p>Please note, these tips specifically relate to my experience as a female front-end developer in a male-dominated field. I am aware that diversity encompasses much more than that, and that people of colour, trans people, LGBTQ+ people and those with disabilities frequently experience discrimination, and to greater degrees. I can only speak to my own experiences, but if you are a person from a marginalised group and would like to add your voice to this article, feel free to reach out and I will be more than happy to make some edits.</p>
<p>So, here are my top tips for hiring (and retaining!) diverse teams:</p>
<h2>Acknowledge the problem</h2>
<p>If you’ve read this far, congratulations – you’re probably someone who <em>cares</em> about having a diverse team, and that’s the first step! Many people don’t want to ackowledge the issue at all, and don’t see having a team dominated by young, white males as a problem. There are lots of reasons why we <em>should</em> care, but one of them is that hiring only people with similar life experiences to you, is at best limiting and at worst outright dangerous. A team with a restricted collective experience is less likely to spot problems, oversights or design issues that impact those with different life experiences and, as a consequence, is more likely to perpetuate these problems by implementing inconsiderate products.</p>
<h2>Be an ally</h2>
<p>Recruiting diversely isn’t always easy in the tech industry, as the pool of candidates is overwhelmingly white and male. But companies who care about diversity, and are actively making an effort to attract and retain a diverse workforce feel far more welcoming than the ones that look (from the outside) like an all boys club. Companies that are, mentoring, sponsoring and advocating for minorities in tech are more attractive, and those that are visibly putting into practice anti-discrimination policies (e.g. in their job descriptions) and stamping out bad behaviour are even better.</p>
<p>Go along to meetups (without an agenda) – listen to people’s experiences. Don’t expect them to have all the answers, but make them part of the discussion. Don’t expect a flood of candidates overnight, but cultivate a long-term culture that makes your company attractive to people from all backgrounds.</p>
<h2>Do you really need all those “requirements”?</h2>
<p>Studies show that women are less likely to apply for jobs where they don’t meet 100% of the requirements, while men will apply for jobs where they meet only a few of the requirements. If you pull a long list of “requirements” that aren’t really essential to the role, you’re already cutting out a lot of potential candidates.</p>
<p>The language you use in job descriptions is important. Try running your job ad through this <a href="http://gender-decoder.katmatfield.com/">gender decoder for job ads</a> to see if your job description could be discouraging female applicants.</p>
<h2>Have a robust pay policy</h2>
<p>Too often, both starting salary and subsequent pay rises are opaque and bound up in negotiation. Study after study shows that women are missing out, frequently being paid less than their male counterparts for the same work. Make your pay policy transparent: Set out clear expecations of what work you expect an employee to be able to handle in order to achieve a specific salary. This could be done at the personal level (in employees’ reviews, for example) but is even better if you can make it public within your company.</p>
<h2>Hire juniors</h2>
<figure>
<img src="https://css-irl.info/top-tips-for-hiring-diverse-teams-02.png" alt="A seeding growing into a large flower" />
</figure>
<p>More and more new developers are coming to the field via coding bootcamps, and the great news is they’re bringing more diversity to the industry. But the reality is, when they’re ready to unleash their skills on the world, the number of jobs out there for junior developers is relatively small. If you’re only looking to hire candidates with 5+ years of using the hottest Javascript frameworks, you are going to have a less diverse pool of candidates to draw from.</p>
<p>A great way to hire a diverse workforce is to bring in juniors, invest in their skills and train them up to be great developers.</p>
<h2>Look for good learners</h2>
<p>This is directly related to the previous point. Someone who might not have all the technical skills but is adaptable, and demonstrably a great learner, is more valuable than someone has <em>only</em> technical skills but no aptitide for learning, teaching, mentoring, and team working. Technical skills can be learnt. So-called “soft skills” are much harder to teach someone.</p>
<h2>Cultivate a culture of feedback</h2>
<p>A good manager should regularly review their employees and help them be the best that they can be. Bake these reviews into the company culture, don’t just do them on an ad-hoc basis. If an employee is struggling, understand why that is, and what you can do to help. Help them set out clear action points to handle their current workload better, or advise on responsibilities they could take on to help them progress to the next level.</p>
<p>And very importantly, when your employees are doing great work, tell them! Happy, valued employees are more invested, and make for a team that everyone wants to be a part of. Conversely, only ever hearing negative feedback is demoralising and will make employees desert you in droves.</p>
<h2>In-work training and mentoring</h2>
<p>Training is an important part of enabling employees to thrive, and web development doesn’t sit still. Employees and companies who don’t have an awareness of the latest technologies will get left behind. Web development is moving at lightning speed these days, and it is often assumed that employees who are passionate about the job will find a way to keep up with new developments in their spare time, by undertaking personal projects, for example.</p>
<p>Not only is this an incredibly unhealthy attitude (there’s a reason why rate of burnout is so high in our industry), it’s also an enormous assumption about level of priviledge. Women in particular are more likely than men to have caring responsibilities outside of work (whether for their own child, or another dependant, friend or familiy member). Even women who don’t have these responsibilities are far more likely to do the lion’s share of domestic and <a href="https://www.bbc.co.uk/bbcthree/article/5ea9f140-f722-4214-bb57-8b84f9418a7e">emotional labour</a> (such as booking doctors’ appointments, remembering to buy birthday gifts, and dealing with household admin, in addition to household chores), and effectively go home to work a second shift.</p>
<p>All this can leave very little time for personal development outside of working hours. If you don’t make time for this during the working day, you are severely limiting employees’ opportunities to progress, and perhaps even damaging their health.</p>
<p>There are several forms that in-work training can take:</p>
<ul>
<li>You could set aside an afternoon per week for structured learning, or experimental projects. Plan these out in advance, so they benefit the company as a whole, and don’t result in wasted time.</li>
<li>You could set aside an annual training budget (both time and money), which would enable employees to attend conferences.</li>
<li>A company blog can be a good way to enable employees to focus on learning a small skill, and write about it so that it benefits everyone.</li>
<li>If an employee wants to speak at conferences, you could support them by helping them with their proposal, or scheduling in a practice-and-feedback session.</li>
</ul>
<p>Be transparent about career progression opportunities. If they are looking for ways to progress their career and achieve that next salary milestone, set out clear objectives that they would need to meet in order for that to happen.</p>
<h2>Parental leave</h2>
<p>How you treat employees who are also parents (or have caring responsibilities) says a lot about your company. Regardless of whether or not they want to have a baby, <strong>women do not want to have to ask about your parental leave policies in the interview</strong>. I cannot stress this enough. I and many other women I know have resisted asking about this, for fear of being judged as less commited to the job. Have a generous maternity and paternity leave policy, actively encourage <em>all</em> new parents to take it (regardless of gender), and don’t for a second make assumptions about when they should or shouldn’t return to work. Someone who feels supported and confident in their job is far more likely to return to work, and work better, than someone who feels overlooked or expendible. Support them, discuss a plan with them, by all means. Don’t make them fear for their jobs while pregnant or on parental leave.</p>
<p>And, for goodness sake, <strong>put it at the top of your list of company “perks”</strong>. Spell it out <em>in detail</em>. It’s far more important than your pool table or free beer fridge. Speaking of which...</p>
<h2>Be mindful of your office perks</h2>
<p>Job ads that emphasise a “fun” company culture, table football, treating everyone “like a family”, and (of course) free beer are a big red flag, and can be exclusionary. You’re more likely to get a bunch of applicants with very similar life experiences. The biggest office perk is paying your employees well, treating them with respect, and enabling them to go home on time at the end of the day.</p>
<p>That’s not to say you <em>shouldn’t</em> have office perks – but be mindful about what they say about you. If you’re emphasising them over and above the actual day-to-day working environment, there’s something wrong.</p>
<h2>Facilitate remote working</h2>
<p>Remote working has never been easier, and many companies these days have a 100% remote team. Even if that’s not right for you (and it’s not for everyone – there are benefits to an on-site team too!), then it could help to attract a more diverse pool of candidates by embracing a partially remote approach. Perhaps you could (optionally) offer a partly remote working week, or try it out on selected projects.</p>
<p>Remote working can greatly help with work-life balance – as long as you don’t abuse it by expecting employees to be logged on at all hours – and help ease the burden on employees who have external responsibilities by cutting out commuting time.</p>
<h2>Part time working</h2>
<p>There is absolutely no reason in 2019 why the vast majority of web design and development jobs can’t work on a part-time or job-share basis. This will open up opportunities for many people who have health conditions, disabilities or responsibilities which mean they would struggle with a full-time role.</p>
<h2>Be flexible with holiday and unpaid leave</h2>
<p>For employees who have children, being able to take days off when they need to for emergencies can be a godsend. If you can enable this <em>without</em> eating into their holiday allowance that’s even better. You could consider offering unpaid leave in addition to regular holiday allowance, which could be capped or uncapped.</p>
<h2>Be mindful of implicit bias</h2>
<p>Bias is not always intentional. The fact is, as a society we’re conditioned to hold implicit (or unconscious) biases, which, studies show, disadvantage women and minorities in many cases. It can take a lot of personal effort to be aware of our own biases and act upon them. <a href="https://www.theguardian.com/uk-news/2018/dec/02/unconscious-bias-what-is-it-and-can-it-be-eliminated">This article</a> suggests that writing down priorities beforehand can help assess candidates more objectively and eliminate unconscious bias. This applies to employee reviews, as well as the interview and selection process.</p>
<h2>Be the change you want to see</h2>
<p>In my previous job, both the company directors cut back their working hours to four days per week so that they could spend more time with their families. When people in senior and management roles take a lead on this, it sends a great signal to employees that their personal lives are valued, and they’ll feel much more comfortable discussing working hours if they’re struggling, or negotiating hours during the selection process. As with many of the other points above, this can really benefit people with additional needs and responsibilites, and make your workplace far more attractive.</p>
Subgrid is here2019-05-26T00:00:00Zhttps://css-irl.info/subgrid-is-here/<figure>
<img src="https://css-irl.info/subgrid-is-here.png" alt="An example grid layout with 4 large and 4 small items" />
</figure>
<p>Support for <a href="https://www.w3.org/TR/css-grid-2/#subgrids">subgrid</a> (part of the CSS Grid Level 2 specification) has just landed in Firefox Nightly! To start experimenting with it you’ll need to enable the feature by going to <a href="about:config">about:config</a> in the browser, then searching for <em>subgrid</em>. Toggle <em>layout.css.grid-template-subgrid-value.enabled</em> and <em>subgrid</em> to <em>true</em>.</p>
<p>It’s still very early days, but just a couple hours of trying it out have got me really excited about the layout possibilities that this will bring. Once subgrid is more widely supported I think it will open the door for some really interesting, creative layouts.</p>
<p>Here’s a quick demo I’ve been playing around with today:</p>
<iframe height="404" style="width: 100%;" scrolling="no" title="Subgrid test" src="https://codepen.io/michellebarker/embed/gJzxBW/?height=404&theme-id=0&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/gJzxBW/">Subgrid test</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>Creating a subgrid is pretty simple. A child of a parent grid needs <code>display: grid</code>, and either <code>grid-template-columns: subgrid</code> or <code>grid-template-rows: subgrid</code>. The subgrid can be on the row axis, the column axis or both. The CSS for a very basic example might look something like this:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.grid</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token property">grid-template-columns</span><span class="token punctuation">:</span> <span class="token function">repeat</span><span class="token punctuation">(</span>3<span class="token punctuation">,</span> 1fr<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">grid-template-rows</span><span class="token punctuation">:</span> <span class="token function">repeat</span><span class="token punctuation">(</span>3<span class="token punctuation">,</span> 200px<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.grid-item</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token property">grid-template-columns</span><span class="token punctuation">:</span> subgrid<span class="token punctuation">;</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> 1 / span 2<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.subgrid-item</span> <span class="token punctuation">{</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> 2 / 3<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>There are already some <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout/Subgrid">docs on MDN</a> written by <a href="https://rachelandrew.co.uk/">Rachel Andrew</a> if you’re keen to get started.</p>
<p>I’m looking forward to spending a lot more time experimenting and building some creative layouts – and I’ll definitely be writing more about it soon!</p>
How Git Stash Can Help You Juggle Multiple Branches2019-05-25T00:00:00Zhttps://css-irl.info/how-git-stash-can-help-you-juggle-multiple-branches/<p>When juggling multiple branches in Git, it’s easy to accidentally start working on the wrong branch, before realising you need to switch to another. Or sometimes you might be working on a feature and you’re not ready to commit your changes yet, when someone asks you to fix an urgent bug and you need to jump onto another branch. This is where Git’s <code>stash</code> command comes in useful.</p>
<h2>Creating and applying a stash entry</h2>
<p>Stashing allows us to save a copy of our uncommitted changes on the current working branch.</p>
<figure>
<img src="https://css-irl.info/how-git-stash-01.svg" alt="A stash entry added to the stash list" />
</figure>
<p>In its simplest form, the <code>git stash</code> command creates a stash entry. To reapply our stashed changes at a later point, we can use <code>git stash apply</code>.</p>
<p>Create a stash:</p>
<pre><code>git stash
</code></pre>
<p>Apply a stash entry to your current working branch:</p>
<pre><code>git stash apply
</code></pre>
<p>We can apply the stash entry to a different branch – it doesn’t have to be the branch that we created the stash from.</p>
<h3>Stashing untracked files</h3>
<p>By default, <code>git stash</code> will only stash the <em>tracked</em> files. If we want to create or apply a stash entry including <a href="https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository"><em>untracked</em> files</a> (e.g. files that have not previously been staged, or files that are in our <em>.gitignore</em>), we can add the <code>-u</code> (or <code>--include-untracked</code>) flag to our command:</p>
<p>Create a stash entry including untracked files:</p>
<pre><code>git stash -u
</code></pre>
<p>To apply a stash entry including untracked files:</p>
<pre><code>git apply -u
</code></pre>
<h2>Multiple stash entries</h2>
<p><code>git stash apply</code> will apply the last stash entry you created to your current working branch. But it’s possible to store multiple stashes at the same time, and to apply them individually. To list all the stash entries, use <code>git stash list</code>. This will bring up a list that looks something like this:</p>
<pre><code>git stash list
stash@{0}: WIP on my-branch: ca96af0 Commit message 3
stash@{1}: WIP on my-branch: 03af20c Commit message 2
stash@{2}: WIP on my-branch: 216b662 Commit message 1
</code></pre>
<p>By default, stash entries are named WIP (Work in Progress), followed by the branch and commit the stash entry was created from. This might not be very useful if we have multiple stash entries – it’s not very easy to see what changes we’ll be applying! Instead, we could save our stash with a custom message, so it’s easier to see what it relates to:</p>
<pre><code>git stash save 'my brand new stash'
</code></pre>
<p>Now, when we list our stash entries, we’ll see our custom message instead of the generic one:</p>
<pre><code>git stash list
stash@{0}: On my-branch: my brand new stash
stash@{1}: WIP on my-branch: ca96af0 Commit message 3
stash@{2}: WIP on my-branch: 03af20c Commit message 2
stash@{3}: WIP on my-branch: 216b662 Commit message 1
</code></pre>
<p>To apply a particular stash entry from our list, we can reference it with the <em>apply</em> command:</p>
<pre><code>git stash apply stash@{2}
</code></pre>
<p>(Replace the last part with whichever stash reference we wish to use.)</p>
<p>Subsequent stash entries are added to the beginning of the stash list. The most recent stash will have the reference <code>stash@{0}</code>.</p>
<p>The stash list can contain stash entries from different branches, which could each be applied to other branches in your project.</p>
<figure>
<img src="https://css-irl.info/how-git-stash-02.svg" alt="Stashes created from multiple branches" />
</figure>
<h2>Applying vs. popping</h2>
<p>Applying a stash entry will keep a copy in the stash list – so we could apply the same stash entry to multiple branches. If we run <code>git stash list</code> after applying the stash, we’ll see the stash we applied is still there.</p>
<p>List all our current stashes:</p>
<pre><code>git stash list
</code></pre>
<p>If we want to remove a stash entry from the list when we apply it, we could use <code>pop</code> instead of `apply:</p>
<pre><code>git stash pop
</code></pre>
<p>This works similarly to <code>apply</code>, where it will pop the last stash entry by default Or we could instead pop an individual stash:</p>
<pre><code>git pop stash@{2}
</code></pre>
<p>Popping is probably a good idea if you know you don’t need to apply your stash entry on any other branches, and you want to keep your stash list nice and clean.</p>
<h2>Creating a new branch with a stash applied</h2>
<p>We can use <code>branch</code> to a new branch and apply the most recent stash entry:</p>
<pre><code>git stash branch
</code></pre>
<p>This is basically a shortcut for the following:</p>
<pre><code>git checkout -b my-new-branch
git stash apply
</code></pre>
<p>Again, it can take a reference to a specific stash entry if you wish to apply a different entry:</p>
<pre><code>git stash branch stash@{2}
</code></pre>
<h2>Removing and clearing stashes</h2>
<p>It’s a good idea to keep a clean store and remove the stash entries we don’t need anymore, especially when moving between branches. Life will be much simpler if we only have a handful of stashes to pick from, rather than hundreds! Plus once our changes are commited, we don’t really need those stashes anyway.</p>
<p>We can remove individual stashes from the stash list by using the <code>drop</code> command. As with <code>pop</code> and <code>apply</code>, this will affect the latest stash entry by default. If you want to target a specific stash then we can pass it the stash reference:</p>
<pre><code>git stash drop stash@{2}
</code></pre>
<p>Alternatively we can clear all our stash entries at once:</p>
<pre><code>git stash clear
</code></pre>
Part 2: What the Fr(action)?2019-05-16T00:00:00Zhttps://css-irl.info/debugging-css-grid-part-2-what-the-fraction/<p>In the second part of the <em>Debugging CSS Grid</em> series, we’ll take a look at <em>fr</em> (or <em>fraction</em>) units. <em>Fr</em> units are very useful for sizing grid tracks, and vastly simplify the process of building responsive layouts. But there are one or two unexpected behaviours you may run into if you don’t understand how they work. This article will aim to demystify these.</p>
<h2>Introduction</h2>
<p>The <em>fr</em> unit is a new unit, exclusive to Grid. It allows you to size your grid tracks according to a proportion of the available space in the grid container. By using <em>fr</em> units instead of percentages for a flexible layout, we can avoid messy and complicated <em>calc()</em> functions to size our grid tracks. As a simple example, we can create four equal-width columns:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.grid</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token property">grid-template-columns</span><span class="token punctuation">:</span> <span class="token function">repeat</span><span class="token punctuation">(</span>4<span class="token punctuation">,</span> 1fr<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">column-gap</span><span class="token punctuation">:</span> 20px<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<figure>
<img src="https://css-irl.info/debugging-css-grid-2-03.png" alt="Three grid items of 200px and one grid item of 1fr" />
<figcaption><em>Fig 01</em> Four equal width tracks (each sized at 1fr)</figcaption>
</figure>
<p>The grid takes into account the 20px gap between each column track and distributes the remaining space equally. You can also use it alongside fixed tracks:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.grid</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token property">grid-template-columns</span><span class="token punctuation">:</span> <span class="token function">repeat</span><span class="token punctuation">(</span>3<span class="token punctuation">,</span> 200px<span class="token punctuation">)</span> 1fr<span class="token punctuation">;</span>
<span class="token property">column-gap</span><span class="token punctuation">:</span> 20px<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<figure>
<img src="https://css-irl.info/debugging-css-grid-2-02.png" alt="Three grid items of 200px and one grid item of 1fr" />
<figcaption><em>Fig 02</em> The 1fr column on the right expands to fill all of the remaining space, once the fixed tracks are taken into account.</figcaption>
</figure>
<p>This will give us three fixed columns of 200px and a fourth column, sized with the <em>fr</em> unit, which will take up the remaining space.</p>
<p>We can use multiples of the <em>fr</em> unit to create tracks that are proportionally larger or smaller. In this example, the second track will be twice the width, and the fourth track will be three times the width of the first and third tracks.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.grid</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token property">grid-template-columns</span><span class="token punctuation">:</span> 1fr 2fr 1fr 3fr<span class="token punctuation">;</span>
<span class="token property">column-gap</span><span class="token punctuation">:</span> 20px<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<figure>
<img src="https://css-irl.info/debugging-css-grid-2-01.png" alt="Four grid items of differing widths" />
<figcaption><em>Fig 02</em></figcaption>
</figure>
<h2>All fr units are not created equal</h2>
<p>A common mistake is to assume that all tracks sized with the same number of <em>fr</em> units will be the same size. This is certainly what you would expect if you were using percentages for track sizing, for example. But if we compare the first and last examples above, we can quite clearly see that the <em>1fr</em> columns in the last example (<em>Fig 03</em>) are <em>not</em> the same size as those in the first example (<em>Fig 01</em>), despite using the same value! The reason for this is that <em>fr</em> units are <em>flexible</em> units. They do not behave as lengths, like pixels, rems, ems and others, which is why they cannot be used in <code>calc()</code> functions. To quote directly from the spec:</p>
<blockquote>
<p>Tracks sized with fr units are called “flexible tracks”, as they flex in response to leftover space similar to how flex items fill space in a flex container.</p>
</blockquote>
<p>Flexible tracks are resolved last according to Grid’s sizing algorithm. The browser takes into account all of the fixed tracks and column or row gaps, plus the maximum size of any tracks sized using expressions like <code>minmax()</code>, then distributes the remaining space accordingly.</p>
<p>Consider the following example:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.grid</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token property">grid-template-columns</span><span class="token punctuation">:</span> <span class="token function">repeat</span><span class="token punctuation">(</span>3<span class="token punctuation">,</span> <span class="token function">minmax</span><span class="token punctuation">(</span>20px<span class="token punctuation">,</span> 300px<span class="token punctuation">)</span><span class="token punctuation">)</span> 1fr<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>We have three columns sized with <code>minmax()</code> (with a maximum size of 300px), plus one column of <em>1fr</em>. If the width of the grid container is less than the sum of the three columns (900px) then the last column’s maximum size will depend on the content. If the track contains no grid item (or the grid item has no content, and nothing else affecting its size, like padding or borders) then it will have a resolved width of 0 – so it will be invisible. It’s only when our grid container is larger than 900px (e.g. for larger viewports) that we will see that <em>1fr</em> column, which will fill the remaining space in the grid.</p>
<iframe height="419" style="width: 100%;" scrolling="no" title="minmax() and fr" src="https://codepen.io/michellebarker/embed/MdpBME/?height=419&theme-id=0&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/MdpBME/">minmax() and fr</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<h2>Fractions of fractions</h2>
<p>You don’t need to distribute <em>all</em> of the available space in a grid. We can also size tracks using values of less than 1fr.</p>
<p>If we have three grid tracks at 0.5fr each, we might expect that they take up half the width of the available space – a fraction of a fraction. But this demo shows what actually happens here.</p>
<iframe height="397" style="width: 100%;" scrolling="no" title="Fractions of fractions" src="https://codepen.io/michellebarker/embed/mYWyjR/?height=397&theme-id=0&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/mYWyjR/">Fractions of fractions</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>The tracks with a size of 0.5fr actually behave as if they were 1fr! This might be somewhat surprising if we think of <em>fr</em> tracks in the same way as length-based units (like percentages), but becomes clearer if we think of these as flex items instead.</p>
<h3>Understanding the flex factor</h3>
<p>The value of the fr unit in the CSS Grid specification is referred to as the <a href="https://www.w3.org/TR/css-grid-1/#grid-template-columns-flex-factor">flex factor</a>. The value of any <em>fr</em> tracks is computed by this formula:</p>
<pre><code><flex factor of the track> * <leftover space> / <sum of all flex factors>
</code></pre>
<p>The specification explains what happens when a track’s flex factor is less than 1:</p>
<blockquote>
<p>If the sum of the flex factors is less than 1, they’ll take up only a corresponding fraction of the leftover space, rather than expanding to fill the entire thing.</p>
</blockquote>
<p>Because each of our tracks is 0.5fr, the sum of all our flex factors is greater than 1 – 1.5 to be exact. So our column tracks expand to fill all the available space. However, if we sized each track at 0.2fr, say, then the sum of the flex factors will be 0.6. If we try this out then we can see that each item will take up the equivalent proportion of the available space.</p>
<iframe height="372" style="width: 100%;" scrolling="no" title="Fractions of fractions" src="https://codepen.io/michellebarker/embed/BeWNQP/?height=372&theme-id=0&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/BeWNQP/">Fractions of fractions</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<h2>Intrinsic and extrinsic sizing</h2>
<p>We’ve seen that the size of <em>fr</em> tracks is influenced by the rest of the grid: The sizes of other tracks, and the <code>gap</code> values. This is known as <em>extrinsic</em> sizing – where the size is determined by context. But the size of an <em>fr</em> track is also dependent on its content. If you have three columns of 1fr, and you place an item in one of those columns whose horizontal size is larger than the equal distributed space then that track will grow to accommodate the content, while the others will become smaller to make space. This is <em>intrinsic</em> sizing. (The <a href="https://www.w3.org/TR/css-sizing-3">Intrinsic and Extrinsic sizing specification</a> offers a full explanation.)</p>
<p>In this example we have a grid with three child items, and one of those children contains an really long word:</p>
<iframe height="439" style="width: 100%;" scrolling="no" title="Fr units" src="https://codepen.io/michellebarker/embed/vwyyWV/?height=439&theme-id=0&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/vwyyWV/">Fr units</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>We can see that the column containing the longer word is larger than the other two tracks, despite being sized with the same unit. (The same thing will happen if you have some content in the grid with its own intrinsic dimensions – e.g. an <code><img></code> element with <code>width: 600px</code> in the CSS.)</p>
<p>This is a sensible behaviour and prevents our content from being cut off, or overflowing the container. But it’s not always desireable. If the purpose of our grid is to impose a strict visual layout, then this has the potential to break our layout. If we want to clamp our grid tracks so that they take up an equal proportion of the available space regardless of the size of their content, we can use CSS Grid’s <code>minmax()</code> function. By default, Grid effectively behaves as if 1fr tracks have a minimum size of auto – <code>minmax(auto, 1fr)</code>. By supplying a different minimum (e.g. <em>0</em>), we can prevent our grid tracks expanding to fit the content. You can see this in action in the following example:</p>
<iframe height="407" style="width: 100%;" scrolling="no" title="Fr units with minmax()" src="https://codepen.io/michellebarker/embed/ZNBeQw/?height=407&theme-id=0&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/ZNBeQw/">Fr units with minmax()</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<h2>Conclusion</h2>
<p>Fr units are actually the simplest units to work with in Grid, and for the most part cause much less pain than using percentages and <em>calc()</em> for your grid tracks! Don’t be put of using them! I hope this article can serve as a handy reference if you ever get caught out in some more unusual scenarios.</p>
<h2>Further reading</h2>
<p><a href="https://www.smashingmagazine.com/2018/04/best-practices-grid-layout/">Best Practices with Grid Layout</a> by Rachel Andrew</p>
<p><a href="https://www.smashingmagazine.com/2018/01/understanding-sizing-css-layout/">Understanding Sizing in CSS Layout</a> by Rachel Andrew</p>
Part 1: Understanding Implicit Tracks2019-04-30T00:00:00Zhttps://css-irl.info/debugging-css-grid-part-1-understanding-implicit-tracks/<aside>
<p>This post was edited on <time datetime="2019-07-22">22 July 2019</time> to better describe item placement in the example of overlapping grid items.</p>
</aside>
<p>When observing people getting to grips with CSS Grid, I’ve noticed a few issues that catch people out more often than others, or present more of a challenge when it comes to building a layout. This short series of articles will delve into these common problems and aim to provide a better understanding of Grid so that you can anticipate layout problems, and debug them more easily when they occur.</p>
<h2>Accidental implicit tracks</h2>
<p>The biggest issue I’ve seen people struggle with is accidentally creating extra grid tracks, which can throw an entire layout into disarray. These extra tracks are known as <em>implicit</em> tracks, and they are created by placing an item outside of the <em>explicit</em> grid’s boundary. To get the most out of Grid, it’s a good idea to understand the concepts of the <em>explicit</em> and <em>implicit</em> grid, and their relationship to each other.</p>
<h3>The explicit grid</h3>
<p>The explicit grid is defined using the <code>grid-template-rows</code> and <code>grid-template-columns</code> properties (or shorthand <code>grid-template</code> if you prefer):</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.grid</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token property">grid-template-rows</span><span class="token punctuation">:</span> <span class="token function">repeat</span><span class="token punctuation">(</span>4<span class="token punctuation">,</span> 150px<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">grid-template-columns</span><span class="token punctuation">:</span> <span class="token function">repeat</span><span class="token punctuation">(</span>4<span class="token punctuation">,</span> 1fr<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Here we’re defining a grid with four rows and four columns, and we can know that our grid will have <em>at least</em> four rows and four columns, no matter what. Even if we don’t have any grid children to place, so that our grid is completely empty, it will still take up the space of four rows and four columns that we’ve defined above.</p>
<p>If we used <code>repeat(4, auto)</code> for the <code>grid-template-rows</code> property, our grid rows would all have a height of <code>auto</code> – so if we had no grid children then our rows would still exist, they would just collapse down to a height of zero without any content to fill them. If we added a row gap (e.g. <code>row-gap: 40px</code>) then the combined height of the gaps between the rows would make up the height of our grid – so without any content it might look like an extra large margin or padding value somewhere that was breaking your layout!</p>
<h3>What are implicit tracks?</h3>
<p>Implicit tracks are tracks that are only created by placing items. This behaviour in Grid is intentional, and very useful. An example is if you have a grid with four columns that we want to fill with an indeterminate number of items (e.g. a news feed. If we don’t know the number of items, we won’t know how many rows we need for the grid. By default, grid items are placed into the next available grid cell. We can simply omit the <code>grid-template-rows</code> property and allow Grid’s auto-placement to create the right number of rows for our content.</p>
<p>(Side note: I’m assuming here that the grid is using the default <code>grid-auto-flow: row</code>. If you change this to <code>grid-auto-flow: column</code> then implicit tracks will be created on the row axis instead.)</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.grid</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token property">grid-template-columns</span><span class="token punctuation">:</span> <span class="token function">repeat</span><span class="token punctuation">(</span>4<span class="token punctuation">,</span> 1fr<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<figure>
<img src="https://css-irl.info/debugging-css-grid-1_03.png" alt="Seven grid items laid out over two rows" />
<figcaption>Our grid has four columns. The items fill the first row, then a new one is created.</figcaption>
</figure>
<p>We can control the behaviour of implicit tracks with <code>grid-auto-rows</code> and <code>grid-auto-columns</code>.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.grid</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token property">grid-template-rows</span><span class="token punctuation">:</span> <span class="token function">repeat</span><span class="token punctuation">(</span>4<span class="token punctuation">,</span> 150px<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">grid-template-columns</span><span class="token punctuation">:</span> <span class="token function">repeat</span><span class="token punctuation">(</span>4<span class="token punctuation">,</span> 1fr<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">grid-auto-rows</span><span class="token punctuation">:</span> 150px<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>The above code, in addition to defining four explicit column and row tracks, instructs Grid that any implicit row tracks created should have a fixed height of 150px. This property is optional, and without it any implicit tracks will have a default size of <code>auto</code>.</p>
<h3>Placing items</h3>
<p>To place an item on the grid we’ve just created, we could do something like this:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.item</span> <span class="token punctuation">{</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> 1 / 2<span class="token punctuation">;</span>
<span class="token property">grid-row</span><span class="token punctuation">:</span> 3 / 5<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>We’re using start and end lines to place the grid item at the bottom left of our grid.</p>
<figure>
<img src="https://css-irl.info/debugging-css-grid-1_06.png" alt="Orange grid item placed at the bottom left of the grid" />
</figure>
<p>This is not going to cause any problems because we are explicitly placing items by grid line number. We know that our grid has four rows and four columns (therefore five grid lines in either direction), so we’re unlikely to accidentally to unintentionally use a higher line number and accidentally create implicit tracks.</p>
<p>Alternatively, we could use the <em>span</em> keyword in place of a start or end line:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.item</span> <span class="token punctuation">{</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> 1 / 2<span class="token punctuation">;</span>
<span class="token property">grid-row</span><span class="token punctuation">:</span> 3 / span 2<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>I like using <code>span</code> for grid placement – it’s often helpful when you know an item needs to span a set number of grid tracks, rather than end at a specific line – but it means you can sometimes lose track of which grid line you’re placing an item on.</p>
<p>Here we’re using <code>span</code> in place of the <code>grid-row-end</code> line. If we change the <em>span</em> value to 3 instead of 2, this would cause the item span more row tracks than there are available – and whoops! We’ve created an implicit track!</p>
<figure>
<img src="https://css-irl.info/debugging-css-grid-1_04.png" alt="Orange grid item placed at the bottom left of the grid" />
</figure>
<p>One place this problem sometimes occurs is when you want grid items to overlap each other. Items that aren’t explicitly placed are placed into the next available grid cell, and if there isn’t a grid cell available then implicit tracks will be created, rather than items being stacked on top of each other. This behaviour is very useful as it means we don’t always need to explicitly place items, but this is one case where it’s not particularly helpful to us!</p>
<p>A friend of mine was using Grid to position two elements, one on top of the other, but offset by one row:</p>
<figure>
<img src="https://css-irl.info/debugging-css-grid-1_01.png" alt="A grid layout with two overlapping items" />
</figure>
<p>This is the code that was used to create the layout:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.grid</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token property">grid-template-columns</span><span class="token punctuation">:</span> <span class="token function">repeat</span><span class="token punctuation">(</span>4<span class="token punctuation">,</span> 1fr<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">grid-template-rows</span><span class="token punctuation">:</span> <span class="token function">repeat</span><span class="token punctuation">(</span>3<span class="token punctuation">,</span> 200px<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.item:first-child</span> <span class="token punctuation">{</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> span 4<span class="token punctuation">;</span>
<span class="token property">grid-row</span><span class="token punctuation">:</span> 1 / span 2<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.item:nth-child(2)</span> <span class="token punctuation">{</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> span 4<span class="token punctuation">;</span>
<span class="token property">grid-row</span><span class="token punctuation">:</span> 2 / span 2<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Instead of the desired layout, we get this:</p>
<figure>
<img src="https://css-irl.info/debugging-css-grid-1_02.png" alt="A grid layout with the second item pushed to the right" />
</figure>
<p>What has happened to the second item? Can you spot the problem here? Both items are using the <em>span</em> keyword for the <code>grid-column</code> value. The first item will be positioned correctly because it will be auto-placed in the first available cell with a span of 4. The second item doesn’t have a start or end line, so the browser needs to resolve this, which it does by generating implicit column tracks.</p>
<p>If the second item only had a <em>span</em> value and no start line on the row axis, then it would wrap onto the next row because the item would still be participating in the flow of the grid. This wouldn’t be the layout we want, but would perhaps be less baffling! But in the code above the browser doesn’t know which column we want to place the item in. It resolves this by placing the item starting at line 5 on the column axis and generating four implicit tracks.</p>
<p>Because we’re not using <code>grid-auto-columns</code> to define a size for our implicit tracks, these will have a default size of <code>auto</code>. And if the grid item has no content, then those tracks will collapse down to a width of 0, rendering our item invisible. Our grid item contains some text, so these implicit tracks will be auto-sized to accommodate this.</p>
<p>If we had a <code>column-gap</code> value of, say, <code>20px</code>, we would see the width of two column gaps be added to our grid, although the tracks themselves would have a width of 0.</p>
<p>The solution here is to place both items explicitly with a start and end value:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.item:first-child</span> <span class="token punctuation">{</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> 1 / span 4<span class="token punctuation">;</span>
<span class="token property">grid-row</span><span class="token punctuation">:</span> 1 / span 2<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.item:nth-child(2)</span> <span class="token punctuation">{</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> 1 / span 4<span class="token punctuation">;</span>
<span class="token property">grid-row</span><span class="token punctuation">:</span> 2 / span 2<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Play with the demo below to explore different ways of “breaking” the layout:</p>
<iframe height="413" style="width: 100%;" scrolling="no" title="Grid placement example" src="https://codepen.io/michellebarker/embed/yrwjpb/?height=413&theme-id=0&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/yrwjpb/">Grid placement example</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<h2>Preventing our layouts breaking</h2>
<p>So, how can we best avoid running into problems with implicit tracks? One way is to understand how Grid is calculating our layout behind the scenes.</p>
<h3>Understanding the Grid Item Placement Algorithm</h3>
<p>This sounds scarier than it really is! The <a href="https://www.w3.org/TR/css-grid-1/#auto-placement-algo">Grid Item Placement Algorithm</a> is the order in which the placement of grid items is resolved. Grid items that are explicitly positioned first, followed by the items with a definite row position, then the browser determines the columns in the implicit grid and places any auto-placed items (items without an explicit position) accordingly. This is assuming the <code>grid-auto-flow</code> property value is <code>row</code> (the default). Keeping this in mind can help you to understand why you might have implicit tracks being created on one axis and not the other, if this is contrary to your expectations.</p>
<p>I also have a few tips for ways you to place items than can help avoid accidentally pushing items off the explicit grid...</p>
<h3>Naming grid lines</h3>
<p>One way that helps me be more intentional with my grid placement is naming grid lines. Let’s say the item we want to place is an image. We could do this:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.grid</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token property">grid-template-rows</span><span class="token punctuation">:</span>
<span class="token function">repeat</span><span class="token punctuation">(</span>2<span class="token punctuation">,</span> 150px<span class="token punctuation">)</span> [image-start] <span class="token function">repeat</span><span class="token punctuation">(</span>2<span class="token punctuation">,</span> 150px<span class="token punctuation">)</span>
[image-end]<span class="token punctuation">;</span>
<span class="token property">grid-template-columns</span><span class="token punctuation">:</span> [image-start] <span class="token function">repeat</span><span class="token punctuation">(</span>3<span class="token punctuation">,</span> 1fr<span class="token punctuation">)</span> [image-end] 1fr<span class="token punctuation">;</span>
<span class="token property">grid-auto-rows</span><span class="token punctuation">:</span> 150px<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Using <em>-start</em> and <em>-end</em> as a suffix for our line names creates a grid area, which makes placing our image very simple:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.image</span> <span class="token punctuation">{</span>
<span class="token property">grid-area</span><span class="token punctuation">:</span> image<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>You can also use the <code>grid-template-areas</code> property to do a similar thing, which feels more intuitive to many people – just bear in mind that won’t work if you need overlapping grid items.</p>
<h3>Placing by end line</h3>
<p>Sometimes placing by end line number (as opposed to start line number) can help avoid the problem of creating accidental implicit tracks. Taking our example above, perhaps we know that we want the image to span three grid tracks, so we’re using the <em>span</em> keyword as the <code>grid-column-end</code> value. But it might be better to use <em>span</em> as the <code>grid-column-start</code> value and explicitly place it on its end line:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.image</span> <span class="token punctuation">{</span>
<span class="token comment">/* The image will end at line 4 on the column axis: */</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> span 3 / 4<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>This can be helpful if we have a very large grid. Imagine our grid has 20 columns instead of just four, we might know that it needs to be placed one line away from the end, but we don’t want to have to calculate what the start line should be each time – that would be annoying and prone to error!</p>
<h3>Negative grid lines</h3>
<p>A technique I find very useful (and something I’ve <a href="https://css-irl.info/negative-grid-lines/">written about before</a>), is using negative line numbers to place grid items. Negative line number represent the lines of your grid in reverse. So in a grid of four tracks (which would have five grid lines), line -1 is the equivalent to line 5, line -2 is the equivalent to line 4, and so on.</p>
<p>Again, this can come in very handy when working with a large grid. If we know and item needs to align to the end of the grid then we can simply use grid line -1, instead of having to remember that the last line is line 21, for example.</p>
<h3>Debugging with dev tools</h3>
<p>I thoroughly recommend the Firefox dev tools for inspecting and debugging problems with CSS Grid. The <a href="https://developer.mozilla.org/en-US/docs/Tools/Page_Inspector/How_to/Examine_grid_layouts">grid inspector</a> allows you to switch on line numbers, so even if the sizes of your implicit tracks have collapsed right down to zero you will still be able to see that they have been created. (The inspector also shows you the negative line numbers – very handy!)</p>
<h2>Conclusion</h2>
<p>I hope this article goes some way towards demystifying exmplicit versus explicit tracks when working with CSS Grid and equips you with some valuable knowledge to help you debug broken layouts. Look out for more articles in the <em>Debugging CSS Grid</em> series coming soon.</p>
Controlling Leftover Grid Items with Pseudo-selectors2019-04-13T00:00:00Zhttps://css-irl.info/controlling-leftover-grid-items/<p>I recently wrote about <a href="https://css-irl.info/to-grid-or-to-flex">some of the cases where you might want to use Grid instead of flexbox</a>, and vice-versa. One of the scenarios I pointed out <em>might</em> be a better case for using flexbox is when you want to control the behaviour of any leftover grid items that don’t fill an entire row.</p>
<figure>
<img src="https://css-irl.info/controlling-leftover-grid-items-01.svg" alt="10 items on a 4x4 grid" />
<figcaption>As there are only 10 items in this grid rather than 12, we may want to control how those last two items are displayed.</figcaption>
</figure>
<p>In the typographic world, words at the end of a paragraph that don’t take up a full line are called <a href="https://www.fonts.com/content/learning/fontology/level-2/text-typography/rags-widows-orphans">widows</a>. These grid items behave in a similar way, so that’s how I’m referring to them here. (Side note: The CSS properties <code>widows</code> and <code>orphans</code> deal with these typographic behaviours in paged media and multi-column layout.)</p>
<h2>Why would we want to use Grid here?</h2>
<p>To my mind, using grid is often the better choice when it comes to defining a fixed number of columns that each need to take up a proportion of the available space. We can use the <em>fr</em> unit here, which is designed for this purpose:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.grid</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token property">grid-template-columns</span><span class="token punctuation">:</span> <span class="token function">repeat</span><span class="token punctuation">(</span>3<span class="token punctuation">,</span> 1fr<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">grid-gap</span><span class="token punctuation">:</span> 20px<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>This will give us three equal width columns that utilise all the space available. Doing this with Grid is a lot cleaner than the flexbox solution, which would require <code>calc()</code> and negative margins to get the same effect. In this demo, the first example uses flexbox and the second uses Grid to achieve the same layout:</p>
<iframe height="374" style="width: 100%;" scrolling="no" title="Flexbox vs Grid layout examples" src="https://codepen.io/michellebarker/embed/xeXgqy/?height=374&theme-id=0&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/xeXgqy/">Flexbox vs Grid layout examples</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>In this case, our Grid items don’t require us to place them explicitly. They will all be placed into the next available cell using Grid’s default auto-placement, which is helpful if we don’t know the number of items in our Grid.</p>
<p>The problem arises if we want to control the behaviour of any leftover items. If there is just one widow, perhaps we want it to fill the entire row, or maybe we’d prefer to align it to the right instead of the left. Or if there are two items, maybe we want to center them:</p>
<figure>
<img src="https://css-irl.info/to-grid-or-to-flex-01.svg" alt="Two flexbox layout examples" />
</figure>
<p>We can’t achieve this by relying solely on auto-placement, but we <em>can</em> still get the behaviours we want using with only a little bit of extra code.</p>
<h2>nth-child concatenation</h2>
<p>By combining <code>:nth-child()</code> and <code>:last-child</code> pseudo-selectors, we can detect whether an item is a widow or not and adjust our styles accordingly. <a href="http://www.heydonworks.com/">Heydon Pickering</a> demonstrated a similar technique, which he refers to as <em>quantity queries</em>, in <a href="https://alistapart.com/article/quantity-queries-for-css/">this A List Apart article</a>. We’re going to use it slightly differently here, because we’re not querying <em>how many</em> items there are. We want to detect whether an item is both a last-child <em>and</em> comes immediately after a child that is a multiple of three (i.e. it’s the first item in a row). (We can’t use <code>:last-child</code> alone, as this would select the last item regardless of whether it’s a widow or not.)</p>
<p>Then we can target that item with our styles, e.g. setting it to span three grid tracks:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">li:last-child:nth-child(3n - 2)</span> <span class="token punctuation">{</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> span 3<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>You can see it in action in this demo:</p>
<iframe height="415" style="width: 100%;" scrolling="no" title="CSS Grid + nth-child to control last row behaviour" src="https://codepen.io/michellebarker/embed/KEXErp/?height=415&theme-id=0&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/KEXErp/">CSS Grid + nth-child to control last row behaviour</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>In the first of the two examples shown I’m targeting the last child item if it is also the <em>second</em> item in a row and making that span two columns, while the second example targets the last child if it is the <em>first</em> item in the row:</p>
<pre class="language-css"><code class="language-css"><span class="token comment">/* Target the second item on the last row, as long as it is the last item in the grid */</span>
<span class="token selector">li:last-child:nth-child(3n - 1)</span> <span class="token punctuation">{</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> span 2<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">/* Target the first item on the last row, if it is the last item */</span>
<span class="token selector">li:last-child:nth-child(3n - 2)</span> <span class="token punctuation">{</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> span 3<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h3>Centering the items</h3>
<p>Using flexbox for this layout would allow us to center our items easily by using <code>justify-content: center</code> on the container, which would allow the one or two remaining grid items to be centered instead of spanning multiple columns:</p>
<figure>
<img src="https://css-irl.info/controlling-leftover-grid-items-02.svg" alt="A grid with the two leftover items centred" />
</figure>
<p>This might be a nicer option in some cases, as making a grid item wider can draw more attention to it and make it seem more important, when perhaps this is not the intention.</p>
<p>We can achieve this with grid too – we just need a couple more small steps in our process. First we’re going to give our grid six columns instead of three:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.grid</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token property">grid-template-columns</span><span class="token punctuation">:</span> <span class="token function">repeat</span><span class="token punctuation">(</span>6<span class="token punctuation">,</span> 1fr<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">grid-gap</span><span class="token punctuation">:</span> 20px<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Then we need to make each item span two columns instead of one:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">li</span> <span class="token punctuation">{</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> span 2<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Using a <em>span</em> value rather than a start or end line number allows us to still take advantage of Grid’s auto-placement – we don’t need to explicitly place the items. We’re telling our grid that they should each span two tracks, but otherwise flow naturally into the available grid cells.</p>
<p>Then we can target the last and last-but-one grid items as before, but instead adjust their <code>grid-column-end</code> line instead of their span:</p>
<pre class="language-css"><code class="language-css"><span class="token comment">/* Dealing with 2 orphan items */</span>
<span class="token selector">li:last-child:nth-child(3n - 1)</span> <span class="token punctuation">{</span>
<span class="token property">grid-column-end</span><span class="token punctuation">:</span> -2<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">li:nth-last-child(2):nth-child(3n + 1)</span> <span class="token punctuation">{</span>
<span class="token property">grid-column-end</span><span class="token punctuation">:</span> 4<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">/* Dealing with single orphan */</span>
<span class="token selector">li:last-child:nth-child(3n - 2)</span> <span class="token punctuation">{</span>
<span class="token property">grid-column-end</span><span class="token punctuation">:</span> 5<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>This gives them the effect of being centered.</p>
<iframe height="417" style="width: 100%;" scrolling="no" title="CSS Grid + nth-child" src="https://codepen.io/michellebarker/embed/aMVLxb/?height=417&theme-id=0&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/aMVLxb/">CSS Grid + nth-child</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<h2>Conclusion</h2>
<p>There are still some instances where it might be more convenient to use Flexbox for a layout like this, which become more apparent when implementing it responsively. The above examples won’t work if you’re using CSS Grid’s <code>auto-fill</code> and <code>auto-fit</code> keywords instead of a fixed number of column tracks, because (without some fairly complex calculations) you can’t be sure of how many items will be on a grid row at any one time. <code>auto-fill</code> and <code>auto-fit</code> are quite useful in that they can deliver a responsive layout without the need for media queries – your grid responds to the containing block. Using flexbox allows us to achieve a similar thing, albeit with some hacks to accommodate gutters.</p>
<p>The techniques detailed in this post can still be useful, and give you one more tool in your toolbox that can help you make an informed decision when building layouts.</p>
Amending Your Past Commits with Git2019-04-08T00:00:00Zhttps://css-irl.info/amending-your-past-commits-with-git/<p>Have you ever pushed some code with a bad commit message and wished you could go back in time and edit it? Perhaps you got two different commits mixed up, or maybe your commit message was insufficiently descriptive. Either way, bad commit messages are no good to anyone – you never know when you might need to check out a commit, and hunting through past commits for an elusive chunk of code can be a nightmare. Your future self won’t thank you for it!</p>
<p>What if you need to do more than just edit a commit message? There are plenty of times I’ve accidentally included the wrong file in a commit, or else missed one change and had to push an extra commit to rectify it.</p>
<p>With Git, there are ways we can go back and edit our past commits. We can change the commit message, or add or remove files if we need to. Let’s look at a few ways to do just that.</p>
<h2>Amending your last commit</h2>
<p>If the commit you want to change is the very last one you made, and the commit hasn’t been pushed yet, then amending it is very simple. Just type:</p>
<pre><code>git commit --amend
</code></pre>
<p>If you run this with the <code>-m</code> flag, you can edit your commit message in the terminal at the same time:</p>
<pre><code>git commit --amend -m "Edited commit message"
</code></pre>
<p>Then edit your commit message, save the commit, and push your code to the repository. You can also add or remove files by making those changes <em>before</em> executing the <em>amend</em> command. Here of code we’re adding the file <em>README.md</em> and removing the file <em>wrong-file.md</em>, then editing the commit message:</p>
<pre><code>git add README.md
git rm wrong-file.md
git commit --amend -m "Edited commit message"
</code></pre>
<p>You could also make changes to your last commit (such as adding or removing files) <em>without</em> changing the commit message:</p>
<pre><code>git add README.md
git rm bad-file.md
git commit --amend --no-edit
</code></pre>
<h2>Amending older commits</h2>
<p>But what if you’ve already pushed your commit, or even if it’s a few commits back? Happily, there’s a fairly straightforward way to edit your past commit messages – assuming you know which commit the one you want to edit is. We’ll need to use the <code>rebase</code> command – but first, let’s try to understand a little bit about it.</p>
<h3>Travelling through time</h3>
<p>Using Git is a bit like having access to our own time machine. We can move backwards and forwards on our timeline by checking out different commits. If we’re working in a team we might have lots of people working on their own separate timelines (<a href="https://git-scm.com/book/en/v2/Git-Branching-Branches-in-a-Nutshell">Git branches</a>), and sometimes their timelines might converge with our own (<a href="https://www.atlassian.com/git/tutorials/using-branches/git-merge">merging</a>), but time is basically linear. If you commit some bad code you can always go back in time to before it happened. With every commit you’re adding some more steps to the timeline – so if something goes wrong, you haven’t lost all that great work you did. Pretty cool!</p>
<figure>
<img src="https://css-irl.info/amending-your-past-commits-with-git-01.png" alt="Illustration showing a git master branch in the centre with two other branches merging in a linear fashion" />
<figcaption>A linear Git history</figcaption>
</figure>
<h3>Rebasing</h3>
<p>Rebasing is the process of moving the base of your branch <a href="https://www.atlassian.com/git/articles/git-forks-and-upstreams">upstream</a>. If you’ve based your current feature branch off of the <em>master</em> branch and the <em>master</em> branch has since moved on because of other contributions, then you can perform a rebase to ensure you have the latest changes from <em>master</em> on your current branch. You <em>could</em> use <code>git pull</code> or <code>git merge</code>, but this would result in a single (potentially very large) commit being tacked onto your branch’s history at the point you pull those changes. With rebasing, we insert the extra commits from the <em>master</em> branch into our timeline, so it looks like they were there all along.</p>
<figure>
<img src="https://css-irl.info/amending-your-past-commits-with-git-02.png" alt="Illustration showing the master branch being merged into the feature branch" />
<figcaption>Merging an upstream master branch into your feature branch results in a large commit in the feature branch</figcaption>
</figure>
<figure>
<img src="https://css-irl.info/amending-your-past-commits-with-git-03.png" alt="Illustration showing the feature branch being rebased from master" />
<figcaption>Rebasing moves the base of your feature branch along to include the new commits from the master branch</figcaption>
</figure>
<p>Rebasing opens up some other dimensions for us. Time is no longer linear – rebasing takes us into the world of the multiverse. We can actually change history. That is to say, we can go back, change (or remove) a commit and it’s like it never happened. Our past, present and future will be like that commit never existed at all. If you’ve watched the Netflix series <a href="https://www.netflix.com/gb/title/80211627">Russian Doll</a>, perhaps you can see how different timeline can operate in parallel! Let’s look at how this great and terrible superpower can be useful.</p>
<h2>Keeping a clean history</h2>
<p>You might think it’s great to have a linear timeline where everything that has ever happened is recorded and set in stone. But keeping a clean Git history has advantages. It can make it much quicker and easier to look back and see the chunks of work that was done on a project or feature, which is useful if you’re reviewing a team’s code contributions. It can make it simpler to locate and fix bugs too as it’s less likely you’ll need to wade through irrelevant commits.</p>
<h2>Editing the timeline</h2>
<p>The <code>git rebase</code> command has two modes: manual and interactive. To edit our past commits we have to do an <a href="https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History">interactive rebase</a>. We’ll need to know how many commits back we need to go (relative to the <em>HEAD</em>, or current commit). Let’s say that the third from last commit is the one we want to edit. Type the following into your terminal:</p>
<pre><code>git rebase -i HEAD~3
</code></pre>
<p>This will bring up a list of the last three commits. For example:</p>
<pre><code>pick cd16d77 Adding .gitignore
pick 67c91dc Adding README.md
pick 1ba6af9 Removing unneccessary imports
</code></pre>
<p>Alternatively, if you know the commit ID, you could target that instead:</p>
<pre><code>git rebase -i cd16d77
</code></pre>
<p>You’ll notice each commit has the word <code>pick</code> next to it. We need to change this to <code>reword</code>. Use the <code>i</code> key as a shortcut for insert mode, which will allow us to edit the text. Then you can change the actual text of the commit message alongside it.</p>
<p>If you want to change not only the commit message, but the content of the commit itself, you can use <code>edit</code> instead of <code>reword</code>. You’ll notice there are some other options too, including <code>drop</code> to remove a commit entirely. (These also have their short commands: <code>d</code> for <em>drop</em>, <code>r</code> for <em>reword</em>, etc.)</p>
<p>One you’re happy with your commit, hit <em>escape</em> to exit insert mode, then type <code>:wq</code> to exit the Git editor.</p>
<p>If you haven’t yet pushed your bad commit, then we don’t need to do anything further. Otherwise, we’ll need to force push. If, like me, you’re a bit scared of Git, then force-pushing is just about the scariest thing you can do. You’re re-writing Git history. But lets face our fears together!</p>
<p>First, a caveat: force-pushing is only something you should do on your own branch. If you force push to a shared branch then you’re rewriting the history for other people too, and they will have to resolve this locally, potentially causing big headaches. (This is why it’s a really good idea to work on separate branches!). If you’re happy that no-one else is going to be affected then you can go ahead and run:</p>
<pre><code>git push --force
</code></pre>
<p>When rebasing the HEAD will automatically be reset to the latest commit, so we’re done – we’ll already have the most up-to-date version of our project to continue working on.</p>
<p>So there you go, we’ve rewritten history and the world didn’t end! If you’re interested in learning more, Atlassian has some great tutorials, including <a href="https://www.atlassian.com/git/tutorials/rewriting-history">this one</a> about rebasing and rewriting history.</p>
<p>I’m trying to learn more about Git and would love to hear your tips! If you have something to share, please let me know on Twitter <a href="https://twitter.com/CSSInRealLife">@CSSInRealLife</a>.</p>
<p>I made a <a href="https://github.com/mbarker84/git-tips">small repository</a> that I’m using to play around with Git while I’m learning, that also includes a handful of commands and explanations, which I’m adding to over time. Feel free to fork it and use it for your own Git practise (e.g. by adding files to the <em>playground</em> directory), or create a pull request if you have some useful things to add!</p>
How to Create Better Themes with CSS Variables2019-04-01T00:00:00Zhttps://css-irl.info/how-to-create-better-themes/<p>Published on LogRocket</p>
Building a Dependency-free Site in 20192019-03-22T00:00:00Zhttps://css-irl.info/building-a-dependency-free-site/<figure>
<img src="https://css-irl.info/building-a-dependency-free-site.jpg" alt="My personal website screenshot" />
<figcaption>A screenshot of my new, improved personal website</figcaption>
</figure>
<p>After several years of basically ignoring it, I decided recently that it was time for a refresh of <a href="http://michellebarker.co.uk/">my personal site</a>. The previous iteration used a combination of Gulp and Bower, coupled with Susy, a Sass library for the grid system (version 2, rather than the latest version 3 release). The last time I did any work on it was around 2015 and, suffice to say, my tools were pretty out-of-date. This just wasn’t going to cut it in 2019.</p>
<p>I didn’t want to spend a whole lot of time configuring a new set of tools for what I intended to be a very simple single-page site. I didn’t plan on using Javascript, although I wouldn’t rule this out down the road (as progressive enhancement). I wanted to get my site built and published as quickly as possible, so that I’d have something to point people to if they want to know what I do. But I also wanted to be able to maintain it relatively easy – add extra speaking engagements, articles and bio updates when the need arises. It was important to me that re-visiting the site a little way down the road wouldn’t require a whole new re-configuration of a complex set of tools – I didn’t want to spend an hour updating dependencies before I could do any actual work. I want my tools to get out of the way so I can concentrate on the things I love: HTML and CSS.</p>
<p>At the core, a HTML file and a CSS file are all you need to build a website. (And of course, a CSS file is optional! But there aren’t many websites you’d want to build without one.) But the last time I built a site without <em>any</em> kind of tooling was probably five or six years ago. I’ve become pretty dependent on Sass, and figured that writing CSS without it would be quite frustrating. However, CSS has moved on significantly in the intervening years. We now have CSS variables (or custom properties), which allow us to store and reuse property values throughout our CSS file. We also have the CSS Grid Layout specification, which makes it incredibly simple to build layouts – there’s no longer any need to import a dependency like Bootstrap for its grid system, and you can cut out quite a lot of CSS (notwithstanding some fallbacks you might have to write for older browsers).</p>
<p>Not only that, but code editors have come along in leaps and bounds too. With VS Code, it’s very easy to search for keywords within your file or project, find-and-replace values and lint your code. A whole plethora of extensions allow things like autocompletion, autoformatting, and even picking colours right from the editor.</p>
<p>So maybe writing vanilla CSS wouldn’t be so burdensome after all. I decided to try building my site in HTML, CSS and nothing else.</p>
<p>Naturally there are some advantages of automated tools that I’ve had to forgo. I can’t minify my files, lazyload images or inject critical CSS, all of which would give a performance advantage. But on balance I’m ok with that, as the site is pretty lightweight anyway. I’m not using any full-width images, and the images I am using are compressed sensibly, so their file sizes are small. In fact, it scores 100% for performance using Google’s <a href="https://developers.google.com/web/tools/lighthouse/">Lighthouse</a> tool for analysis – better than anything I’ve ever built before!</p>
<h2>A systematic approach</h2>
<p>My first port of call was semantic HTML. I wanted the site to be as accessible as possible. This informed the design too, which is quite simple and minimalist, without extraneous clutter. One thing I was interested in experimenting with however, is the grid system. A lot of sites default to a 12-column layout, which of course has its advantages. But I always like the idea of using asymmetric grids, which feel fresh and different. I used the same asymmetric grid for each section of my site, and nested two- or three-column grids for the blocks of links for articles, speaking engagements and sites I’ve worked on.</p>
<h3>CSS</h3>
<p>One thing I really missed from Sass is being able to separate your files into partials. Writing all the CSS in a single file isn’t the easiest experience, even with all the advantages of modern editors. In both my HTML and CSS files I was careful to group related styles using spacing and comments, to make it easier to find the relevant blocks. I used <a href="http://getbem.com/introduction/">BEM</a> for naming my CSS classes, which helped with this.</p>
<p>I found that thinking systematically lends itself well to the single-CSS-file approach. I didn’t employ a CSS reset (e.g. Normalize), which I would usually reach for almost without thinking about it. I leaned on default styling, (e.g. only changing font sizes when the defaults didn’t look right) and configured some global and base-level rules, which I then only overrode when I needed to using something akin to Harry Roberts <a href="https://www.hongkiat.com/blog/inverted-triangle-css-web-development/">ITCSS</a> methodology. I only added classes when necessary, and named these carefully to ensure they would be reusable to an extent. I utilised the cascade and inheritance when possible, and let CSS do the hard work for me. This is in contrast to an atomic CSS approach (which I’d been using in my previous job for the best part of a year, with <a href="https://css-irl.info/a-year-of-utility-classes/">Tailwind</a>), which favours composition over inheritance.</p>
<p>For a very large site, it might be that the systematic approach I took is less practical and has some performance drawbacks, but it was ideal here.</p>
<h2>Drawbacks</h2>
<p>In addition to partials, I really missed nesting from Sass. I don’t advocate going overboard with nesting, but when it comes to media queries, <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/@supports">feature queries</a> and pseudo-selectors, there’s no doubt in my mind that nesting makes writing CSS a lot more manageable. I also missed being able to use variables in media queries, which you can’t do with CSS variables. In short, there are a lot of reasons to keep using Sass in my work, and I don’t plan on stepping away from it just yet.</p>
<p>This simplified approach for building a website clearly doesn’t lend itself to dynamic content. Several people have suggested using a static site generator. I’m fully aware of the benefits of SSGs (this blog uses one), but it was a deliberate decision not to use one in this case. The point was to build fast, with as minimal tooling as possible, and I don’t feel like it will be too hard a task to keep such a small site up-to-date right now. The whole site took around four hours to design <em>and</em> build – I imagine that configuring an SSG would have taken a good proportion of that time.</p>
<p>That said, I wouldn’t rule out an SSG for the future, and I might consider adding dynamic content later on. It would be nice to pull in blog articles and speaking engagements without having to update manually.</p>
<h2>Utilising the code editor</h2>
<p>I use VS Code, and one Twitter user recommended an extension that allows you to <a href="https://github.com/wojciechsura/easysass">compile Sass to CSS in your editor</a>, without a build pipeline. This was a revelation, as I hadn’t really considered it as something my code editor could do before. It reminds me of Codekit, a GUI tool I used a few years ago for compiling Sass, and is certainly something I’ll try out at some point, although I don’t plan on adding Sass to my personal site at this point in time. It’s pretty impressive just how powerful VS Code is becoming. There are some other areas it could help with my development process too:</p>
<h3>Snippets</h3>
<p>In VS Code you can define reusable code snippets that act as shortcuts for what would otherwise be larger blocks of code. If I was dealing with multiple pages I think I’d find it a bit of a headache having to ensure the HTML for my header and footer was the same everywhere. I could create a snippet, and have them populate any new HTML pages with just a few keystrokes.</p>
<p>Although I didn’t use any snippets in the first iteration of my site, I think they’ll also be really useful for media queries. I could set a few snippets for common breakpoints and save myself a lot of time typing things out. I’m really keen to introduce these to my workflow.</p>
<h3>In-editor minification</h3>
<p>There’s are also a few extension available that can <a href="https://github.com/HookyQR/VSCodeMinify">minify your CSS, HTML and JS</a> with a simple shortcut. It can be beneficial for performance to minify these, and I can imagine this will be very useful if my CSS file grows.</p>
<h2>Conclusion</h2>
<p>I feel like many people will be quick to dismiss this as being completely anti-tooling, which is not the case at all. There are a lot of situations where the complex tools we’ve developed as an industry are good and necessary. I’m fully aware the approach I’ve taken here is unlikely to scale well beyond a a very simple site.</p>
<p>There are clear benefits to a build process, and good reasons why we as developers have come to rely so much on NPM, Webpack, Gulp, Babel and more. But while I wouldn’t recommend building <em>all</em> sites in the way I’ve done with my site, I think we’re often guilty of assuming that because our tools are great solutions for some things, they’re automatically the solution for <em>everything</em>. To paraphrase a famous quote, “When your only tool is a hammer, everything looks like a nail”. Some sites simply don’t need the kind of complex tools we’re accustomed to, and if anything are more performant and easier to build without them.</p>
<p>I have a couple of freelance clients that need their (small) sites re-built, and I’m already thinking that this approach is probably going to be good enough, while previously I wouldn’t have considered it. What’s more, I’ll be confident that the code I write is maintainable, and I can easily add more tooling if I need to down the road.</p>
<p>I’d love to hear if you’ve had any similar thoughts (or if you strongly disagree!), and if you have any recommendations for ways to speed up the development process without adding dependencies. When I tweeted about my site re-build, it became my most popular tweet ever.</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">I re-built my personal site using just HTML, CSS and nothing else, just to see if that’s possible in 2019 and it turns out it totally is 🤷♀️ <a href="https://t.co/2Hv5qQfw5s">https://t.co/2Hv5qQfw5s</a></p>— Michelle Barker (@mbarker_84) <a href="https://twitter.com/mbarker_84/status/1107416868711743490?ref_src=twsrc%5Etfw">March 17, 2019</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>It’s quite something when building a website using the most basic technologies imaginable has somehow become revolutionary.</p>
How to Convince Your Team to Adopt CSS Grid2019-03-09T00:00:00Zhttps://css-irl.info/how-to-convince-your-team-to-adopt-grid/<figure>
<img src="https://css-irl.info/how-to-convince-your-team.svg" alt="A group of people talking and thinking about layouts" />
</figure>
<p>Are you keen to jump into CSS Grid Layout but having trouble convincing the rest of your team (whether your peers or your managers)? Someone asked me recently if I had any advice for convincing a skeptical team to adopt CSS Grid into their workflow. Although I haven’t faced any major barriers on this front myself, it’s a story I hear all too often. You’re ready to dive in and work with the latest modern layout techniques, only for the higher powers to put the brakes on.</p>
<p>Although frustrating, there is some rationality here. Let’s break that down.</p>
<p>As a side note, these thoughts come from my experience in a web agency. I’m not claiming to share everyone’s experience, and other environments may require different approaches. I do think there are <em>some</em> suggestions here that are universally valid, however.</p>
<h2>Why do they need convincing?</h2>
<h3>Browser support</h3>
<p>The most common reason cited for <em>not</em> adopting Grid is browser support. While Grid has around <a href="https://caniuse.com/#search=css%20grid">85% browser support worldwide</a>, it’s that other 15% that gives pause. A good proportion of these users are on IE, which actually supports CSS Grid’s older syntax since IE10. (I’ll leave the question of whether you want to support the old syntax for another day, but here’s a <a href="https://css-tricks.com/css-grid-in-ie-css-grid-and-the-new-autoprefixer/">good article</a> to read if you want to go down that road.) Those users need a layout that is, at the very least, usable. That brings me onto the second concern...</p>
<h3>Time</h3>
<p>If not all browsers support a CSS property, you need provide a suitable fallback. In the case of single properties used on their own (e.g. <code>mix-blend-mode</code>) it can be fairly trivial to write an extra line or two that enables users to still experience your content in a useful (if sub-optimal) way. That’s <a href="https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement">progressive enhancement</a>.</p>
<p>With a whole specification like Grid, if you adopt it as your primary layout strategy, it is going to affect not just an element or two, but your entire web page. So it’s a slightly different story. You need to ensure you provide suitable fallbacks, whatever strategy you use to support older browsers. I’m not going to deny that that sometimes takes a bit of time.</p>
<p>If the rest of your team isn’t familiar with Grid yet, then there is also the time factor to consider when it comes to getting everyone up to speed with a new layout strategy. They might be nervous about investing in that training and taking everyone away from existing projects for a day or so.</p>
<h3>Maintainability</h3>
<p>Some teams might be concerned that when it comes to someone else in your team picking up your project to work on, they will find it more difficult to maintain because you’re using unfamiliar CSS, rather than <em>X</em> framework. Coupled with that, there are a lot of different ways to build a layout with Grid. If one person is using named grid lines while another is using <code>grid-template-areas</code>, for example, it can make for a pretty inconsistent codebase, and potentially a headache for anyone who needs to approach that project afresh.</p>
<p>All of these reasons boil down to time and money. What we need to do is convince your team that Grid can help with both.</p>
<h2>How can Grid help?</h2>
<p>Now let’s look at how using Grid can help with the above concerns and more:</p>
<h3>Saving time on complex layouts</h3>
<p>Grid vastly simplifies the process of building layouts that would previously have required a lot of hacks and polyfills. If you need to hack your way around a complex design using older layout methods then you’re going to be burning valuable time. Sure, you also need to provide a usable fallback for older browsers, but often this doesn’t need to take a significant amount of time.</p>
<p>If your team is writing their own grid framework using older layout techniques, that all requires time and effort too.</p>
<h3>Embracing creativity</h3>
<p>If designers, developers and teams want to push the envelope and build truly creative, modern layouts that stand out from the crowd and embrace a <a href="https://www.zeldman.com/2018/05/02/transcript-intrinsic-web-design-with-jen-simmons-the-big-web-show/">new era of web design thinking</a>, then Grid is integral to that. Grid enables us to build layouts that haven’t been possible with CSS before.</p>
<h3>Better performance</h3>
<p>Many projects import in large, unwieldy CSS frameworks for the grid system. Even the minimal ones can end up adding a lot of extra classes, which all add to the weight of your CSS file. For complex layouts that differ from the “standard” columns and rows you might need to turn to Javascript libraries. In my opinion, we almost certainly shouldn’t need to be shipping extra JS just to handle layout in 2019 (with very few exceptions). CSS Grid can handle many complex cases with very little code.</p>
<p>There is also some indication that <a href="https://blogs.igalia.com/jfernandez/2015/06/24/performance-on-grid-layout/">creating Grid designs with flexbox is less performant</a> – although I’ve been unable to find further resources on this with quite the same level of detail.</p>
<h3>Better maintainability</h3>
<p>Because Grid is just native CSS, there’s no risk that it’s going to break and you’ll have to refactor your project in a year’s time. It’s inherently stable. Browser support is only going to get stronger. Conversely, dependencies <em>do</em> break projects. They need maintaining. You might have to revisit a project in a year or two, only to find that it uses an old grid framework that is no longer actively maintained, or the version you’re using is out-of-date and you can’t find the documentation. Well-known frameworks like Bootstrap are perhaps less likely to have this problem, but they come with the performance trade-offs.</p>
<p>Likewise, investing in your team learning Grid is a secure investment for the future. It’s not a framework that’s going to be obsolete in a few years, it’s foundational CSS that’s here to stay. Those skills are going to be useful for many years to come.</p>
<h2>So, how do you convince your team?</h2>
<h3>Websites don’t have to look the same in all browsers</h3>
<p>I believe the biggest barrier to widespread adoption of Grid is the common misconception that websites have to look the same in all browsers. Unfortunately, it can be the case that managers either believe this to be the case, or fail to communicate otherwise to the client. No one wants to be in a situation where your client opens your beautiful, shiny new website on their ancient, creaking machine running IE9 and is immediately flabbergasted that it fails to live up to the designs.</p>
<p>That means you need to put the case forward for progressive enhancement, and help ensure that that communication happens at higher levels. Make managers and designers aware of the limitations of older browsers, and the cost of implementing designs to look the same everywhere. This shouldn’t be on a project-by-project basis, but a strategy across the whole organisation.</p>
<p>I’m aware of how difficult it sounds to change the mindset of an entire organisation, and it is unlikely to happen overnight. One idea I have seen proposed is for designers to actually design a version of the site with a simplified layout to present to the client as a fallback alongside the fully-supported version, in the same way they would present mobile and tablet versions of the design. That way the client is aware that some browsers will be getting the simpler layout, and there’s no big surprise. Plus the designer can actually design it in a way that looks good, rather than relying on a developer’s interpretation. Although there will inevitably be a bit more design time involved, there could be great savings on the development side. I would love to see this approach become more widespread.</p>
<h3>Try it out</h3>
<p>You don’t <em>have</em> to go all-in with Grid – it’s doesn’t have to be an all-or-nothing approach. One of the best ways to introduce Grid is to start with smaller UI modules. That way you have a chance to visually showcase the benefits and hopefully educate – or at least pique the curiosity of – your other team members. It’s often better to show rather than tell.</p>
<p>There’s nothing wrong with using Grid alongside your existing layout system while people get comfortable with it. That gives you time for the next part...</p>
<h3>Plan a strategy</h3>
<p>As I mentioned earlier, there are many ways to build a layout in Grid. You need to think through how you and your team will implement it to ensure that consistency and maintainability do not become issues. You might decide that once everyone has learnt the basics then they can use any approach they like for getting the job done, or you might decide to only used line numbers for placement and avoid <code>grid-template-areas</code>, for instance, to save confusion. You might decide to create a handful of utility classes for your most common layout needs, or your might decide to keep your grid code tightly coupled to components.</p>
<p>You’ll also need to think about your strategy for browser support. Should you use <code>@supports</code> and wrap all of your Grid code inside that, or only where it’s strictly required? Do your research and come up with a plan. It’s likely that your approach will evolve over time, but you need to demonstrate you’ve thought about it in order to provide the smoothest transition for your team.</p>
<h3>Present a proposal</h3>
<p>Try and engineer an opportunity to present your proposal to your team and/or manager. If you can make others feel like they’re part of the discussion they’re more likely to come on board. Plus there might be some other pitfalls you haven’t thought of, which they can point out and you can overcome together.</p>
<p>It can often be hard to push for change within an organisation. Your best bet is to highlight the good stuff, make sure you consider any downsides, try to pre-empt questions. Lastly, get some allies! It’s much easier to promote change together!</p>
Becoming a Tech Speaker2019-02-23T00:00:00Zhttps://css-irl.info/becoming-a-tech-speaker/<p>In this post I’m taking a brief diversion from my usual CSS topics to discuss how becoming a tech speaker has helped me develop my career and my confidence in myself as a developer, and if you’re on the fence about it, to encourage you to give it a try!</p>
<h2>Why I speak</h2>
<p>In the past year I’ve given five talks about CSS Grid and CSS variables. Before that, I had done a few one-off talks, but nothing more. The reason I decided to start speaking about Grid last year is because I found it so interesting that I just wanted to share it with everyone! Speaking at event is a great way to meet other people and forge new friendships and connections with a shared interest. I always feel a bit awkward attending networking events, but if you’re speaking at an event then you already have a pre-prepared topic, and a reason for people to come and approach you. It takes the awkwardness out of many interactions! Some of the most interesting conversations I’ve had with people have been after giving a talk.</p>
<p>If you’re looking for work then speaking can be a great way to find out about potential companies and roles, and make yourself known to people who might be hiring (now or in the future). I can honestly say that my two jobs as a front end developer have come about as a result of speaking at events. While it of course didn’t guarantee me the role, it meant the people hiring were already pre-disposed to talk to me, as they had had a glipmse of my capabilities already.</p>
<p>Although the thought of speaking to a room full of strangers can be scary (I still get really nervous), overcoming your fears and doing something that scares you is a great feeling, and has been 100% worth it.</p>
<p>Speaking can also give you the chance to attend events that you wouldn’t otherwise be able to. Most conferences will cover your travel expenses as a minimum, and of course you can attend the event for free instead of buying a ticket.</p>
<h2>Picking a topic</h2>
<p>As a developer there are always people more experienced than you on any given topic, and it can be tempting to think you have nothing new to say. But everyone has a different perspective to bring to the table, or a different way of teaching it to others. There is no need to try to talk about something brand new, innovative or clever – unless you already have an idea like that, of course! If you pick a subject you’re really interested in, then your enthusiasm will shine through to your audience, and it’ll be a more interesting talk. The chances are that some of the people in the audience will be hearing about your topic for the first time.</p>
<h2>Choosing an event</h2>
<p>The first few events I spoke at were local meetups, which is great for building confidence. Most meetups have a friendly, supportive community who are keen for you to do well. I really like speaking at meetups as the atmosphere is more relaxed, and often people are less intimidated to ask questions, so you can have some interesting discussions. If you’re ready to move on to speaking at conferences, there are plenty out there that offer mentoring and support to craft your talk.</p>
<p>Getting recommendations from friends and other speakers for events to speak at is always a good idea. They’ll be able to tell you which conferences are well-organised, treat their speakers well and usually have interesting talks.</p>
<h2>Submitting a talk</h2>
<p>Often called a CFP (call for proposals), many conferences have a submission process. Some of the best ones anonymise the process, in the interests of achieving a diverse balance of speakers. You can always start with smaller conferences and work your way up.</p>
<p>On Saturday 2nd March there are events going on all over the world for <a href="https://www.globaldiversitycfpday.com/">Global Diversity CFP Day</a>, designed for helping people from underrepresented groups get into speaking. I’m helping with an event in my local city of Bristol. Even if you’re not 100% sure if you want to become a speaker, please <a href="https://www.globaldiversitycfpday.com/events/110">sign up</a> and find out what it’s all about.</p>
<p><a href="https://2019.bristol.wordcamp.org/">WordCamp Bristol</a> is a conference that is currently looking for speakers. It’s a community conference run by lovely people, and will be a great event to get started with if it’s your first time speaking. I’ve just submitted my application – you have a week left to do the same!</p>
<p>Chris Coyier has put together a <a href="https://conferences.css-tricks.com/">list of upcoming conferences</a>, which might be useful if you’re looking to submit a proposal.</p>
<h2>Speaker training</h2>
<p>I’ve recently joined the <a href="https://events.mozilla.org/techspeakers">Mozilla Tech Speaker programme</a>, which helps people become better tech speakers and submit better talk proposals. I highly recommend applying if you’ve just started out speaking and are looking to improve.</p>
<p>If you need some advice about getting started with speaking, please <a href="mailto:contact@michellebarker.co.uk">email me</a> and I’ll be happy to oblige.</p>
To Grid or to Flex?2019-02-10T00:00:00Zhttps://css-irl.info/to-grid-or-to-flex/<p>A recent <a href="https://twitter.com/chriscoyier/status/1088827201468813312">Twitter thread</a> started by Chris Coyier got me thinking about how people in general interpret the use cases for CSS Grid Layout versus flexbox:</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">For y'all that have an understand of both CSS grid and flexbox, what's your favorite way of explaining the difference?</p>— Chris Coyier (@chriscoyier) <a href="https://twitter.com/chriscoyier/status/1088827201468813312?ref_src=twsrc%5Etfw">January 25, 2019</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>Naturally some of the most insightful replies came from Rachel Andrew and Jen Simmons:</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Flexbox is for one dimensional layout. A row OR a column. Grid is for two dimensional layout. Rows AND columns.</p>— Rachel Andrew (@rachelandrew) <a href="https://twitter.com/rachelandrew/status/1088827732874747910?ref_src=twsrc%5Etfw">January 25, 2019</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">Grid makes actual columns and rows. Content will line up from one to the other, as you ask it to. Flexbox doesn’t. Not only in the second dimension (which is easiest to talk about), but also in the first dimension. Flexbox isn’t for most of the things we’ve been using it for.</p>— Jen Simmons (@jensimmons) <a href="https://twitter.com/jensimmons/status/1089181330133450752?ref_src=twsrc%5Etfw">January 26, 2019</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>However, reading tweets individually doesn’t tell the whole story. In this article I want to unpack when and where you might want to use Grid or flexbox, and some reasons for choosing one or the other.</p>
<p>What surprised me about reading the responses in the thread was the number of people stating that they would only use Grid for page-level layout, and flexbox for everything else. If you take this as a rule, then you’re severely limiting yourself when it comes to Grid’s power. The main piece of advice I would give is to take every design individually, analyse the options available and don’t make assumptions about which technology you need. Here are some of the questions you could ask yourself when it comes to choosing a layout method.</p>
<h2>How much maths do you have to do?</h2>
<p>My own contribution to the thread was this:</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr">If it looks like I’d need to reach for calc() a lot for layout then that’s usually a good sign of when I need Grid over flexbox</p>— Michelle Barker (@mbarker_84) <a href="https://twitter.com/mbarker_84/status/1089182216020742144?ref_src=twsrc%5Etfw">January 26, 2019</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>Often, if you’re having to use <em>calc()</em> a lot to get precise track sizes (factoring in gutters, for example) then it’s worth considering using Grid, as the <em>fr</em> unit will do the heavy lifting for you and save you any number of headaches. While that’s fine as a general principle, it’s not the whole picture. There are cases when you might need Grid, even though your layout doesn’t require <em>calc()</em>. One example might be a fixed-width, two-dimensional layout, where each track is 200px wide - you don’t need <em>calc()</em> to tell you how wide those tracks should be, but you might still want the behaviour of Grid. Likewise, there are cases for using flexbox where you do need <em>calc()</em>, so this can only be interpreted as a guideline.</p>
<h2>One dimension or two?</h2>
<p>A big difference between Grid and flexbox is that Grid allows us to control the placement of items in two dimensions (rows and columns), where flexbox does not. Again, that doesn’t mean you should <em>never</em> use grid for one-dimensional layouts. I’ve often opted to use Grid when I need to prescisely control the size and placement for items in one dimension like in this demo and <a href="https://css-tricks.com/super-power-grid-components-with-css-custom-properties/">accompanying article</a>:</p>
<iframe height="417" style="width: 100%;" scrolling="no" title="CSS Grid components with variables and media queries" src="https://codepen.io/michellebarker/embed/XBPMZZ/?height=417&theme-id=0&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/XBPMZZ/">CSS Grid components with variables and media queries</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<h2>How do you want elements to behave?</h2>
<p>Grid is often the right choice when you need to control the layout in two dimensions. That doesn’t make Grid a better choice for <em>everything</em> though. With Grid you have tracks (rows and columns), cells, and grid areas (a group of more than one cell) and items must be placed in these cells or Grid areas.</p>
<p>Let’s say we have a layout like this:</p>
<p>We have a grid of nine items of equal width placed from left to right in rows of three, and a 20px gap between each item. We could build this with either Grid or flexbox. The Grid code is much simpler and cleaner:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.grid</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token property">grid-template-columns</span><span class="token punctuation">:</span> <span class="token function">repeat</span><span class="token punctuation">(</span>3<span class="token punctuation">,</span> 1fr<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">grid-auto-rows</span><span class="token punctuation">:</span> 200px<span class="token punctuation">;</span>
<span class="token comment">/* Assuming we want our rows to be a fixed height – could be left as the default `auto` if we want the height to respond to the content */</span>
<span class="token property">gap</span><span class="token punctuation">:</span> 20px<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Items are then auto-placed without us having to do anything further. If we want to be really smart about it, we could use Grid’s <em>auto-fit()</em> and <em>minmax()</em> functions to give us a fully responsive layout without the need for media queries – try resizing this example and see what happens.</p>
<iframe height="413" style="width: 100%;" scrolling="no" title="Grid auto-fit example" src="https://codepen.io/michellebarker/embed/bzvGaE/?height=413&theme-id=0&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/bzvGaE/">Grid auto-fit example</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>I recommend watching Heydon Pickering’s video <a href="https://www.youtube.com/watch?v=qOUtkN6M52M">Algorithmic Layouts</a> for an overview of this technique and more.</p>
<p>By contrast, if we were to create this layout with flexbox, we would need to style the actual <em>items</em> as well as the grid container:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.grid</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span>
<span class="token property">flex-wrap</span><span class="token punctuation">:</span> wrap<span class="token punctuation">;</span>
<span class="token property">margin</span><span class="token punctuation">:</span> -10px<span class="token punctuation">;</span>
<span class="token property">width</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span>100% + 20px<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.item</span> <span class="token punctuation">{</span>
<span class="token property">width</span><span class="token punctuation">:</span> <span class="token function">calc</span><span class="token punctuation">(</span><span class="token punctuation">(</span>100% / 3<span class="token punctuation">)</span> - 20px<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">flex</span><span class="token punctuation">:</span> 0 0 auto<span class="token punctuation">;</span>
<span class="token property">margin</span><span class="token punctuation">:</span> 0 10px 20px 10px<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>We need negative margins on the grid container to counteract the fact that the total width of the items would be larger than the container itself, and therefore wrap onto the next line. We also don’t get the same responsive behaviour out of the box and would likely need to use media queries.</p>
<iframe height="419" style="width: 100%;" scrolling="no" title="Flexbox layout examples" src="https://codepen.io/michellebarker/embed/VgXwRJ/?height=419&theme-id=0&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/VgXwRJ/">Flexbox layout examples</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>There are a few different ways to achieve this same layout with flexbox, but they all feel a bit hacky – and that’s because they are. We’re using flexbox for something it wasn’t really designed for – but that doesn’t mean it’s always the wrong choice.</p>
<p>Many modern CSS frameworks use some variation on this method for layouts. As a side note, if you do need to go down this route, I’m a big fan of <a href="https://oddbird.net/susy/">Susy</a>, which handles the maths for you.</p>
<p>So, which is the better choice here – Grid or flexbox? It would seem that Grid has some clear advantages here, but in order to answer this question we need to think about what should happen when we have more than nine items but fewer than 12 (the next multiple which would allow them to fill a row). Do we want the new items to simply sit at the start of the next row like the examples we’ve already seen? Or do we want them to behave differently? Perhaps if there is only one item on the next row we want it to take up all the available space in the row, like example A below. Or perhaps if there are two items then we want them to be centred, like the example B.</p>
<figure>
<img src="https://css-irl.info/to-grid-or-to-flex-01.svg" alt="Two flexbox layout examples" />
</figure>
<p>Using Grid Layout and auto-placement, we only have the option of the last item being placed in the cell on the left as in the earlier examples – assuming the value of the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/direction">direction</a> property is not set to <code>rtl</code> (in which case the placement of item will flow right-to-left, and last item will be placed in the cell on the right). The following items will be placed in the next available grid cells. Flexbox allows items to, well, <em>flex</em>. That means we can control the behaviour of those items using a combination of flex and alignment properties.</p>
<iframe height="414" style="width: 100%;" scrolling="no" title="Flexbox layout examples" src="https://codepen.io/michellebarker/embed/MLVYOq/?height=414&theme-id=0&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/MLVYOq/">Flexbox layout examples</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>So whether you choose Grid or flexbox for the layout above really comes down to how you want your grid items to behave – and there may be different answers for different situations.</p>
<h2>Are you trying to replace flexbox with Grid?</h2>
<p>When I give talks I often get asked when I would use flexbox instead of Grid, and whether we even need flexbox anymore. As we’ve seen in the example above, Grid is not a replacement for flexbox. Both of them co-exist quite happily, and knowing when to use each of them gives even more power to your layouts!</p>
<figure>
<img src="https://css-irl.info/to-grid-or-to-flex-02.jpg" alt="A component built using Grid" />
</figure>
<p>In the component above, I needed to control the placement of the text, image and heading on the column <em>and</em> row axis, and control how they would interact with each other to an extent. The only way to do this satisfactorily would be to use Grid.</p>
<p>But I absolutely would use flexbox to build a desktop navigation menu:</p>
<iframe height="314" style="width: 100%;" scrolling="no" title="Flexbox navigation" src="https://codepen.io/michellebarker/embed/bzvNmL/?height=314&theme-id=0&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/bzvNmL/">Flexbox navigation</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>Here I want to control the flow in a single dimension, and I want the items to be <em>flexible</em> – which flexbox does really well. With flexbox we could opt to have those items wrap or not, and allow them to wrap gracefully if the space doesn’t allow for all items to be displayed on one line.</p>
<h2>How should the layout look in browsers that don’t support Grid?</h2>
<p>If we’re using Grid, the other issue we need to take into account is browser support, and what we want to happen to our layout in browsers that do not support Grid (IE11 and below). My approach to is to use feature queries to cater for these cases. Often (but not always) I opt for a flexbox-based layout as a fallback for older browsers:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.grid</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> flex<span class="token punctuation">;</span>
<span class="token property">flex-wrap</span><span class="token punctuation">:</span> wrap<span class="token punctuation">;</span>
<span class="token comment">/* Rest of the fallback layout code */</span>
<span class="token punctuation">}</span>
<span class="token atrule"><span class="token rule">@supports</span> <span class="token punctuation">(</span><span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">)</span></span> <span class="token punctuation">{</span>
<span class="token selector">.grid</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token comment">/* Rest of the Grid code */</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span></code></pre>
<p>However, if you find yourself spending hours trying to replicate the <em>exact same</em> layout for browsers that don’t support Grid, then there’s probably not a lot of reason to use Grid in the first place. The great thing about Grid is it can do things that flexbox alone can’t do.</p>
<p>We’ve discussed some very simple, common layout examples here and how to implement them with Grid or flexbox. To see some more complex layout examples, have a look at the other articles on this blog, or stay tuned for more posts in the future.</p>
My CSS Grid Wishlist2019-02-03T00:00:00Zhttps://css-irl.info/my-css-grid-wishlist/<p>If you follow this blog you’ll know I’m a big fan of CSS Grid, and without a doubt it’s given us developers more power than ever before when it comes to tackling layout on the web. But there are a few of my CSS layout needs that Grid hasn’t quite managed to fulfill just yet – here’s hoping some of these get implemented down the road!</p>
<h2>Styling row and column gaps</h2>
<p>There aren’t any grid properties that allow you to apply styles directly to the row and column gaps. It would be great to be able to apply something like a background or border style here. At the moment, if you want to do something like the example below, you need to hack your way around it with borders, backgrounds or pseudo-elements on the grid items themselves. You <em>could</em> set a background on your grid and a solid colour on your grid items – that’s all well and good unless you want your items to have a transparent background and the content behind to show through.</p>
<p>Something like the <code>column-rule</code> property (from the Multi-Column spec) would at least be a start.</p>
<h2>Multiple gap values</h2>
<p>I’ve come across a number of cases where it would have been incredibly useful to be able to set multiple value for the <code>column-gap</code> and <code>row-gap</code> properties. In one of my previous articles, <a href="https://css-irl.info/solving-a-tricky-layout-problem/">Solving a Tricky Layout Problem with CSS Grid</a>, rather than creating empty rows I could have done something like this:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.grid</span> <span class="token punctuation">{</span>
<span class="token property">row-gap</span><span class="token punctuation">:</span> 0 40px 40px 0<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<h2>Auto flow patterns</h2>
<p>This is a big one, and probably tricky to implement – but would undoubtedly be extremely useful. Suppose I have a layout like this:</p>
<figure>
<img src="https://css-irl.info/my-css-grid-wishlist-01.png" alt="Alternating grid layout" />
</figure>
<p>On every even row, the two grid items are positioned in the cells on the right, and every odd row they are positioned on the left. I’m allowing Grid to create implicit tracks (which can be specified with the <code>grid-auto-rows</code> property) as I don’t know how many items my grid will contain (which may be the case when dealing with user-generated content).</p>
<p>This is relatively simple to acheive using <code>nth-child</code> when our grid items only span a single row track. However, we run into problem if we want to position items in two dimensions. Now let’s suppose every other item needs to span two tracks, and subsequent items need to move down a row to accomodate:</p>
<figure>
<img src="https://css-irl.info/my-css-grid-wishlist-02.png" alt="Layout with every other item spanning two rows" />
</figure>
<p>Now we run into problems when we try to use <code>nth-child</code>, because there is no way to tell Grid to position the items on the row axis relative to the previous one, as evidenced in this Codepen example:</p>
<iframe height="365" style="width: 100%;" scrolling="no" title="Grid auto flow problems" src="https://codepen.io/michellebarker/embed/rPzLoV/?height=365&theme-id=0&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true">
See the Pen <a href="https://codepen.io/michellebarker/pen/rPzLoV/">Grid auto flow problems</a> by Michelle Barker
(<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>I would love to see a solution similar to the <code>grid-template-areas</code> property, where Grid interprets the “areas” as an <code>nth-child</code>-type pattern:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.grid</span> <span class="token punctuation">{</span>
<span class="token property">grid-template-pattern</span><span class="token punctuation">:</span>
<span class="token string">'. . 1 1 2'</span>
<span class="token string">'. . 1 1 .'</span>
<span class="token string">'1 1 2 . .'</span>
<span class="token string">'1 1 . . .'</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Any following items would simply repeat this pattern.</p>
<h2>Calc() with the fr unit</h2>
<p>The <em>fr</em> unit in Grid is incredibly useful, but at the moment it isn’t possible to use it in combination with <code>calc()</code>. According to the specification, this is to do with <em>fr</em> representing a flex value rather than a length, so I imagine this is unlikely to change any time soon.</p>
<p>I’ve come across one or two situations where it would have been useful to be able to use <code>calc()</code> with the <em>fr</em> unit – Ana Tudor's <a href="https://css-tricks.com/dry-state-switching-with-css-variables-fallbacks-and-invalid-values/">DRY state switching</a> technique is one such case that comes to mind.</p>
<h2>Aspect ratio grid cells</h2>
<p>Now this one looks like it might be a real possibility at some point in the future. There is already <a href="https://github.com/tomhodgins/aspect-ratio-spec">a proposal</a> for an aspect ratio CSS property which, while in the very, very early stages, is eagerly anticipated by many CSS developers. I would like to be able to define the aspect ratio of the grid cells themselves, rather than the child items. (I wrote a bit about it <a href="https://css-irl.info/aspect-ratio-cells/">here</a>, and employed a bit of a hack with CSS variables.)</p>
<h2>Special mentions</h2>
<h3>Subgrid</h3>
<p>Subgrid (where a child grid item can inherit the grid of its parent) must be one of the most-requested CSS Grid features. Happily, that’s coming in the <a href="https://www.w3.org/TR/css-grid-2/#subgrids">Level 2 specification</a>!</p>
<h3>Masonry</h3>
<p>Grid still doesn’t allow us to create a true masonry layout natively in CSS – not without dictating the heights of items in some way. It would be great to be able to do this with CSS, but I don’t think of it really as part of CSS Grid. This type of layout seems to me more like a combination of Grid and flexbox, and something else entirely. I hope that one day we’ll be able to acheive it with CSS alone, but I think we’ll have a while to wait.</p>
A Year of Utility Classes2019-01-28T00:00:00Zhttps://css-irl.info/a-year-of-utility-classes/<figure>
<img src="https://css-irl.info/a-year-of-utilities-01.svg" alt="Utility classes surrounding a shapeless <div>" />
</figure>
<p>Last year at <a href="https://ournameismud.co.uk/">Mud</a> we adopted a utility-first approach to CSS (also known as atomic CSS). Specifically we decided to use <a href="https://tailwindcss.com/">TailwindCSS</a>, a utility class framework, which provides a bunch of classes you can apply to your projects to rapidly build a UI.</p>
<p>Sarah Dayan published a great article last year about the <a href="https://frontstuff.io/in-defense-of-utility-first-css">benefits of utility-first CSS</a>, and I recommend reading that to get a good overview of some advantages to the approach on the whole. In this article I’ll summarise what I’ve learnt from adopting atomic CSS in an agency context over the past year, and where it might or might not be appropriate to apply it.</p>
<h2>What are utility classes?</h2>
<p>Utility classes are CSS class names that serve one particular purpose, and are named as such. Typically a class like <code>.bg-blue</code> would give you <code>background-color: blue</code>, for example. It’s not uncommon to use utility classes within CSS, but on the whole they tend to be used sparingly – at least with well-established methodologies such as BEM and ITCSS.</p>
<p>The thinking around the rise of utility class frameworks turns this on its head, and advocates developing with utility classes first, with classes that cover virtually any common style you might want to apply to an element. There are many utility class frameworks around (<a href="https://tachyons.io/">Tachyons</a> is one example), but Tailwind in particular provides simple class names that walk the fine line between brevity and being sufficiently descriptive. It might take a little while to memorize them at first, but most of the class names are fairly intuitive, and I found that after having a window open with the documentation alongside my project for a couple of days, I hardly needed to refer to them at all. (There’s even a <a href="https://github.com/austenc/vscode-tailwind-docs">plugin for VS Code</a> to access the docs, so you don’t even need to leave your editor! I haven’t used this, so can’t vouch for it.)</p>
<p>A string of classes like <code>block p-1 mb-1 text-white bg-blue hover:bg-red</code> would be the equivalent to the following:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.some-element</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> block<span class="token punctuation">;</span>
<span class="token property">padding</span><span class="token punctuation">:</span> 0.25rem<span class="token punctuation">;</span>
<span class="token property">margin-bottom</span><span class="token punctuation">:</span> 0.25rem<span class="token punctuation">;</span>
<span class="token property">color</span><span class="token punctuation">:</span> #ffffff<span class="token punctuation">;</span>
<span class="token property">background-color</span><span class="token punctuation">:</span> #3490dc<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.some-element:hover</span> <span class="token punctuation">{</span>
<span class="token property">background-color</span><span class="token punctuation">:</span> #e3342f<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>That means that our specificity tree is virtually flat. You’re not really using the “cascading” part of CSS at all – with a few exceptions, as we’ll soon see.</p>
<h2>A word on frameworks</h2>
<p>The words “CSS framework” can be quite misleading, and conjure up different things for different people. For many people those words immediately bring to mind Bootstrap, Foundation and other similar frameworks, which provide HTML components and CSS classes to help you build interfaces. You can end up with a lot of extra CSS, because the frameworks include everything you might need. You can also spend a lot of time overriding the framework’s out-of-the-box styles if you want something that feels more bespoke.</p>
<p>The way utility class frameworks are applied is quite different – although not without issues themselves, as we’ll see.</p>
<h3>Configuration</h3>
<p>One attraction of a utility class framework is that it can make development faster once you’ve become accustomed to the syntax. You don’t need to jump back and forth constantly between you HTML and CSS files, and classes are quick and easy to write – even faster if you use an autocomplete plugin like Intellisense in your editor.</p>
<p>The downside is you need to put in some initial legwork in configuration. Tailwind comes with a config.js file, where you define all your variables: colours, typography, sizing (padding, margins, etc.) and much more. There is a default config, but the chances are you’ll need to customise it quite extensively, and it’ll really pay off to do this upfront. It can feel like you’re spending a lot of time setting up before you’ve written any code, but if you invest in this the next stage becomes a lot quicker and easier.</p>
<p>(If you’re trying out Tailwind for the first time, or you just want to use it for some rapid prototyping then don’t worry, it comes with a default config file, so you can skip this step.)</p>
<h3>Naming is hard</h3>
<p>At Mud we previously used BEM for naming CSS classes. This worked reasonably well on the whole, when applied correctly, but was still prone to human error or (in some cases) misapplication. Often, when going back to an older project, you could see a different approach had been taken by two different people who had worked on it. Tailwind removes such inconsistencies by reducing the danger of anyone going off-piste with their class names.</p>
<p>Using Tailwind means worrying about naming things a lot less. A common problem is naming a component, say <code>.news-card</code> and then find yourself repurposing said component for a completely non-news-related thing. It’s nice not to have to think about naming things. But on the other hand, it’s likely you still have to name your component <em>something</em>, so as a selling point for Tailwind this only gets you so far. It has definitely impacted the way I think about naming though, and I’m much more considerate of reusability these days.</p>
<h3>Utility classes in action</h3>
<p>The first thing you’re likely to notice about utility classes, is that because they’re single purpose, you have to use a lot of them. That means, effectively, putting a shortened version of all your CSS classes into your HTML. That can make selectors pretty long, cumbersome, and quite frankly, ugly. I don’t mind admitting that this was extremely off-putting the first time I tried it, and went against every fibre of my being that railed against putting styles in markup. However, after using Tailwind for a little while it soon felt intuitive, and there’s a lot to be said for being able to look at a block of HTML and see at a glance which styles are being applied.</p>
<p>This isn’t without its downsides. Seeing all your styles displayed as one long string within your HTML can make it more difficult to see when you’ve made an error – duplicate or conflicting selectors, typos and incorrect class names can all go unnoticed. Several times I’ve typed a class name that I thought was correct, only to end up scratching my head, figuring out why my styles weren’t being applied. (I believe there may be a VS Code extension in the works for Tailwind linting, but don’t hold me to that.)</p>
<p>When you have a super long selector string to contend with, Tailwind has another option up it’s sleeve. You can simply extract those classes into your CSS file, like so:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.my-super-class-name</span> <span class="token punctuation">{</span>
<span class="token atrule"><span class="token rule">@apply</span> bg-blue text-white font-bold uppercase px-2 py-1 mx-auto mb-1 border-1 w-200<span class="token punctuation">;</span></span>
<span class="token punctuation">}</span></code></pre>
<p>That’s pretty handy. But then, why not write regular CSS, since we’re writing in our CSS file? Plus Tailwind doesn’t have classes for <em>every</em> possible CSS style, so you might need to write some regular CSS anyway. Suddenly we have three possible ways we might be applying styles:</p>
<ol>
<li>Inline Tailwind classes</li>
<li>Tailwind-in-CSS</li>
<li>Regular old CSS</li>
</ol>
<p>I’ve tied myself into specificity knots far worse with Tailwind than with regular CSS, despite the fact that Tailwind appears to be promoted as a solution for such conundrums.</p>
<p>Another place where I found Tailwind difficult to work with was with component variants. We would often need to build several variants of the same component – e.g. a section containing a heading, a block of text and an image, which might have a few possible layout combinations but would otherwise include all the same CSS. With BEM you might have something like this:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>media-object<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>h2</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>media-object__heading<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>h2</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>figure</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>media-object__figure<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>img</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>media-object__image<span class="token punctuation">"</span></span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>some-image.jpg<span class="token punctuation">"</span></span> <span class="token punctuation">/></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>figure</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>media-object__text-block<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>p</span><span class="token punctuation">></span></span>...<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>p</span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>article</span><span class="token punctuation">></span></span></code></pre>
<p>Class names are descriptive, and making some style changes to all those component variants would usually mean simply updating your CSS in one place. Now imagine you’re using utlity class in your HTML and you want to change every component’s padding from <code>1rem</code> to <code>2rem</code>. You’d need to go into the HTML file for each one, find that utility class and update it (without accidentally updating it in the wrong place). It might be a good idea to extract the styles into a CSS file the moment you find yourself repeating selector strings, but this also throws up some issues as previously mentioned. (I recognise that this won’t be a problem for everyone – if you build your components in React for instance, using Tailwind with Styled Components, you’re likely going to have just the one file and account for your variant by passing in props.)</p>
<h3>Plugins</h3>
<p>Because Tailwind is configured with Javascript, you can do things like writing functions and passing them into your config. The advantage is it gives you all the power of Javascript, and you can also write your own custom utilites as plugins.</p>
<p>With regular CSS you might have a handful of utility classes in a Sass partial that you can apply thoughout your project when the need arises (I’ve often done this with a few reusable typography styles, for example). If you try and apply these alongside Tailwind classes this can end up causing confusion where you might expect styles to be overridden and they aren’t. (Which styles override which depends on the order of your imports.) Having learnt this the hard way, I would recommend writing your own custom utilities as Tailwind plugins rather than writing them in Sass. You get the benefit of having these output alongside all of Tailwind’s regular utilities, as well as being able to access Tailwind’s state and breakpoint syntax (e.g. <code>hover:bg-green</code> would give you a hover state of <code>background-color: #38C172</code>.)</p>
<p>The downside is this can be more of a learning curve for people coming who are less familiar with Javascript. I’m not convinced yet that knowing Javascript should be a prerequisite for writing good HTML and CSS.</p>
<h3>Performance</h3>
<p>Theoretically, utility classes are designed to be re-usable and therefore you <em>should</em> end up shipping a smaller CSS file. A smaller file should equate to better performance. However, left untouched, the framework would generate all the classes you might ever need, and leave you will a whole load of unused CSS, unless you take steps to strip it out. The expectation however, is that you would realistically never ship the entire framework. The documentation details a number of ways to remove unnecessary CSS and reduce the overall file size.</p>
<p>One way that’s recommended in Tailwind’s documentation is using a tool like <a href="https://www.purgecss.com/">Purge CSS</a> as part of your build process. In practice you need to be vigilant about which classes you’re removing, particularly if you have some classes that are being added with Javascript. The Purge whitelist is your friend here – it took me a little while to get this right, and on several occasions I ran into trouble getting it to play nicely with my critical CSS.</p>
<h3>Another thing...</h3>
<p>It’s a little thing, but I really like the media query functions, which you can use in your CSS files. Writing <code>@screen md</code> is so much quicker and nicer than writing regular media queries, or even mixins.</p>
<h3>When NOT to use Tailwind</h3>
<p>Some people advocate using utlities for absolutely <em>everything</em> – and by that I mean creating new utilities whenever they don’t already exist in Tailwind. I absolutely do <em>not</em> recommend this approach. I work a lot with CSS Grid, and attempting to configure a utitity class for every possible layout combination would be crazy, not to mention seriously limiting yourself when it comes to being able to fully utilise Grid’s power. There is a <a href="https://www.npmjs.com/package/tailwindcss-grid">Grid plugin</a> for Tailwind, but even its documentation says:</p>
<blockquote>
<p>It's not really practical to expose all of the power of CSS Grid through utilities, but this plugin is a good example of using CSS Grid to replace a cell-only float or Flexbox grid.</p>
</blockquote>
<p>There are plenty of other CSS properties that it’s just not practical to try and replicate with Tailwind, so you’re probably still going to need regular CSS in some shape or form.</p>
<h2>Conclusion</h2>
<p>Tailwind isn’t the magic bullet to fix all of CSS’s supposed problems, nor does it excuse you as a developer from understanding the cascade. If you’re someone who appreciates CSS’s underlying principles you might find it feels counterintuitive at first, but it’s worth persevering before you decide whether it’s the right approach for you. While I wouldn’t choose to use it for every project, there are clear benefits. I believe adopting Tailwind at Mud was the right decision for the team, making out CSS more reusable, maintainable and performant.</p>
Animating CSS Grid2019-01-14T00:00:00Zhttps://css-irl.info/animating-css-grid/<figure>
<video width="100%" controls="" playsinline="">
<source src="https://css-irl.info/media/animating-css-grid.mp4" type="video/mp4" />
</video>
<figcaption>Animated CSS Grid properties in action (Firefox Nightly)</figcaption>
</figure>
<p>Soooo, <a href="http://jensimmons.com/">Jen Simmons</a> just dropped a surprise bombshell on Twitter – CSS Grid <code>grid-template-columns</code> and <code>grid-template-rows</code> properties are now <em>animatable</em> in Firefox Nightly! Naturally I had to jump in and have a go right away!</p>
<p>Here’s the demo if you want to have a play:</p>
<iframe height="435" scrolling="no" title="grid-template-rows / grid-template-columns animation (Firefox Nightly only)" src="https://codepen.io/michellebarker/embed/oJmZKK/?height=435&theme-id=0&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;">See the Pen <a href="https://codepen.io/michellebarker/pen/oJmZKK/">grid-template-rows / grid-template-columns animation (Firefox Nightly only)</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
Solving a Tricky Layout Problem with CSS Grid2019-01-12T00:00:00Zhttps://css-irl.info/solving-a-tricky-layout-problem/<p>Last year, while working at <a href="https://ournameismud.co.uk/">Mud</a>, I worked on the CSS on a site for <a href="https://wbsl.com/">Warner Brothers Leavesden Park studios</a>. A large part of my contribution involved using CSS Grid to build a variety of component layouts, and the layouts I built for that site have become the subject of some of my talks and articles.</p>
<p>This article is a case study on a particular component with a unique layout and a set of constraints. Building a layout that worked for the large number of requirements, as well as unknown content, required applying lateral thinking and a great deal of problem solving to find the right solution.</p>
<p>One such component looks like this:</p>
<figure>
<img src="https://css-irl.info/solving-a-tricky-layout-problem_01.jpg" alt="A component with a large, centred heading, a block of text on the top right and a large image on the left" />
</figure>
<p>It consists of an image or video, a large heading centered both horizontally and vertically and a block of text. The text block is aligned to the top of the image in this case, but could equally be aligned to the bottom of the image if chosen by the content author.</p>
<p>Not too onerous, you might think, and should be simple enough with modern layout methods. But this component comes with a set of contraints:</p>
<ul>
<li>The heading must be horizontally centered within the component, and vertically centered over the image.</li>
<li>The text block must align to the top or bottom of the image, <em>unless</em> the text is longer than the available space, in which case it should extend upwards (or downwards if aligned to the bottom).</li>
<li>The image dimensions are unknown and not constrained by an aspect ratio – in other words, content authors could upload images of any dimensions, without causing them to be cropped.</li>
<li>The length of the heading and the text block are also unknown.</li>
</ul>
<p>The design itself consisted of a 24-column grid, and multiple variants of the component – where the image and text block could align to various different grid columns. For the purpose of this article we won’t focus on the column axis for now, as the primary focus here is the alignment of grid items on the row axis.</p>
<p>I’ve created a simplified view of the same component, which allows us to more easily see the bounding box for each element. (Imagine the pink outline is the component’s bounding box.)</p>
<figure>
<img src="https://css-irl.info/solving-a-tricky-layout-problem_02.png" alt="Simplified illustration of the component layout" />
</figure>
<p>Let’s assume the following markup for the grid container and direct children (our grid items) – we’ll ignore everying inside those elements for the purpose of this article, so that we can just focus on the layout:</p>
<pre class="language-html"><code class="language-html"><span class="token tag"><span class="token tag"><span class="token punctuation"><</span>article</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>grid<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>grid__heading<span class="token punctuation">"</span></span><span class="token punctuation">></span></span>Heading<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>figure</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>grid__image<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>figure</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>div</span> <span class="token attr-name">class</span><span class="token attr-value"><span class="token punctuation attr-equals">=</span><span class="token punctuation">"</span>grid__text<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>div</span><span class="token punctuation">></span></span>
<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>article</span><span class="token punctuation">></span></span></code></pre>
<p>We have the following CSS to define our grid and place items on the column axis:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.grid</span> <span class="token punctuation">{</span>
<span class="token property">display</span><span class="token punctuation">:</span> grid<span class="token punctuation">;</span>
<span class="token property">grid-template-columns</span><span class="token punctuation">:</span> <span class="token function">minmax</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 1fr<span class="token punctuation">)</span> <span class="token function">repeat</span><span class="token punctuation">(</span>24<span class="token punctuation">,</span> <span class="token function">minmax</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 60px<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token function">minmax</span><span class="token punctuation">(</span>0<span class="token punctuation">,</span> 1fr<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token property">column-gap</span><span class="token punctuation">:</span> 20px<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.grid__heading</span> <span class="token punctuation">{</span>
<span class="token comment">/* Using a negative line for the grid-column-end value allows us to easily center the position of the heading when we have a large grid */</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> 5 / -5<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.grid__image</span> <span class="token punctuation">{</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> 2 / 16<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.grid__text</span> <span class="token punctuation">{</span>
<span class="token property">grid-column</span><span class="token punctuation">:</span> span 5 / -1<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>If you’re curious about the values we’re using for the <code>grid-template-columns</code> property, I have <a href="https://codepen.io/michellebarker/post/css-grid-more-flexibility-with-minmax">an explanation here</a>.</p>
<p>Now we need to define our grid rows. My initial thought was that we could define the <code>grid-template-rows</code> property as follows:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.grid</span> <span class="token punctuation">{</span>
<span class="token property">grid-template-rows</span><span class="token punctuation">:</span> 1fr auto 1fr<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>This gives us a central row with the value of <code>auto</code> for our heading (as we don’t know how long this will be, and we want the track to grow to fit the content), and two surrounding rows of <code>1fr</code>. These two outer rows will take up an equal proportion of the available space. If we set <code>row-gap: 40px</code> (using the shorthand <code>gap</code> here for <code>grid-row-gap</code> and <code>grid-column-gap</code>) then we’ll get a 40px gutter above and below the heading, to maintain space between it and the text block.</p>
<figure>
<img src="https://css-irl.info/solving-a-tricky-layout-problem_03.png" alt="Component with row tracks highlighted" />
</figure>
<p>We can also use flex alignment properties with Grid, which are going to be really useful here. I’m using <code>align-items: center</code> to horizontally centre our grid items. It’s not yet obvious why we need to do that, but we’ll soon see that it becomes more useful if our text content is longer than the available space.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.grid</span> <span class="token punctuation">{</span>
<span class="token comment">/* ...Other grid code */</span>
<span class="token property">grid-template-rows</span><span class="token punctuation">:</span> 1fr auto 1fr<span class="token punctuation">;</span>
<span class="token property">gap</span><span class="token punctuation">:</span> 40px 20px<span class="token punctuation">;</span>
<span class="token property">align-items</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Now we can place the grid items on the row axis:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.grid__heading</span> <span class="token punctuation">{</span>
<span class="token property">grid-row</span><span class="token punctuation">:</span> 2<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.grid__image</span> <span class="token punctuation">{</span>
<span class="token comment">/* From the start of the grid to the end */</span>
<span class="token property">grid-row</span><span class="token punctuation">:</span> 1 / -1<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.grid__text</span> <span class="token punctuation">{</span>
<span class="token property">grid-row</span><span class="token punctuation">:</span> 1<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>All of our items are currently centrally aligned in their grid boxes, but if I use <code>align-self: flex-start</code> on the text block then that item will align to the top of the component, while the heading and image are centrally aligned (due to <code>align-items: center</code>, which we specified on the grid itself).</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.grid__text</span> <span class="token punctuation">{</span>
<span class="token property">grid-row</span><span class="token punctuation">:</span> 1<span class="token punctuation">;</span>
<span class="token property">align-self</span><span class="token punctuation">:</span> flex-start<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>So far so good. It looks like we’ve got this layout nailed. There’s just one problem: When the text block is longer than the space between the heading and the top of the image the component expands to accommodate it, pushing the image and heading downwards (which is what we want) – but unfortunately the bottom of the grid also expands, giving us extra space below the image:</p>
<figure>
<img src="https://css-irl.info/solving-a-tricky-layout-problem_04.png" alt="Showing extra space created at the bottom of the grid" />
</figure>
<p>If we have several components stacked on top of each other then the vertical space between them will be uneven. This isn’t ideal. What we want is for the component to grow vertically at the top (the side of our text block) but not the bottom.</p>
<figure>
<img src="https://css-irl.info/solving-a-tricky-layout-problem_07.png" alt="The same grid with the height of the top row only increased" />
<figcaption>The idea solution: the top row track size increases, but the bottom does not</figcaption>
</figure>
<p>To fix this I had to think creatively! Let’s walk through the solution step-by-step.</p>
<p>First of all we’re going to change the three row track sizes to <code>auto</code>:</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.grid</span> <span class="token punctuation">{</span>
<span class="token property">grid-template-rows</span><span class="token punctuation">:</span> auto auto auto<span class="token punctuation">;</span>
<span class="token property">gap</span><span class="token punctuation">:</span> 40px 20px<span class="token punctuation">;</span>
<span class="token property">align-items</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Then we can add an extra row above our existing rows with a height of <code>1fr</code>. It’s perhaps not as obvious what will happen using the <code>fr</code> unit on the row axis. On the column axis, we know the width of our grid (which will be 100% by default), so it’s easy to imagine a column track of <code>1fr</code> filling a proportion of that space. When it comes to the row axis, the height of our grid in this case will be determined by the height of the tallest grid item – the image – which is currently placed from the first to the last grid line.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.grid</span> <span class="token punctuation">{</span>
<span class="token property">grid-template-rows</span><span class="token punctuation">:</span> 1fr auto auto auto<span class="token punctuation">;</span>
<span class="token property">gap</span><span class="token punctuation">:</span> 40px 20px<span class="token punctuation">;</span>
<span class="token property">align-items</span><span class="token punctuation">:</span> center<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>The row we’ve added is effectively a hidden row – it’s going to collapse to <code>0</code> unless we place content in it.</p>
<figure>
<img src="https://css-irl.info/solving-a-tricky-layout-problem_05.png" alt="Hidden row at the top of the grid" />
</figure>
<p>There are a couple more things we need to do to our grid container before we look at item placement. We’re going to set the <code>row-gap</code> to 0 and add tracks of 40px between the heading row and its adjacent rows. We can also remove <code>align-items: center</code>, as we won’t need it anymore.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.grid</span> <span class="token punctuation">{</span>
<span class="token property">grid-template-rows</span><span class="token punctuation">:</span> 1fr auto 40px auto 40px auto<span class="token punctuation">;</span>
<span class="token property">gap</span><span class="token punctuation">:</span> 0 20px<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Now that our grid has six rows, it’s not so easy to visualise where to place our grid items on the row axis. There are more grid lines to keep track of! Naming our grid lines will be very helpful here. Then we can reference those line names to place our grid items.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.grid</span> <span class="token punctuation">{</span>
<span class="token property">grid-template-rows</span><span class="token punctuation">:</span>
[text-start] 1fr [image-start] auto [text-end]
40px [heading-start] auto [heading-end] 40px
auto [image-end]<span class="token punctuation">;</span>
<span class="token property">gap</span><span class="token punctuation">:</span> 0 20px<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.grid__heading</span> <span class="token punctuation">{</span>
<span class="token property">grid-row</span><span class="token punctuation">:</span> heading<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.grid__image</span> <span class="token punctuation">{</span>
<span class="token property">grid-row</span><span class="token punctuation">:</span> image<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token selector">.grid__text</span> <span class="token punctuation">{</span>
<span class="token property">grid-row</span><span class="token punctuation">:</span> text<span class="token punctuation">;</span>
<span class="token property">align-self</span><span class="token punctuation">:</span> flex-start<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Eagle-eyed readers might notice that our text block now spans two tracks (starting at grid line 1), and our image no longer starts at grid line 1 but at line 2 instead. However, visually nothing has changed.</p>
<p>When we add a longer paragraph of text into the text block, it’s then that we can see the benefit of these changes. The hidden row that we added expands, while the image and heading remain centrally aligned with one another – crucially, <em>without</em> extra space being added at the bottom of the grid!</p>
<figure>
<img src="https://css-irl.info/solving-a-tricky-layout-problem_06.png" alt="Hidden row expanding when text block is longer" />
<figcaption>The top row of the grid expands as the text content grows longer</figcaption>
</figure>
<p>Hopefully it’s more obvious now why we needed to set the <code>row-gap</code> to 0 and instead use extra tracks as our gutters: if we had 40px <code>row-gap</code>, this would be visible at the top of the grid even when the text content was shorter and therefore the first track had collapsed completely. Unfortunately we can’t set different values for the <code>gap</code> properties on a single axis, otherwise we wouldn’t need those extra rows.</p>
<p>For a different component variant – where the text block is below the heading instead of above – we can just change the <code>grid-template-rows</code> property to include the hidden row and the end instead of the start.</p>
<pre class="language-css"><code class="language-css"><span class="token selector">.grid--text-bottom</span> <span class="token punctuation">{</span>
<span class="token property">grid-template-rows</span><span class="token punctuation">:</span>
[media-start] auto 40px
[heading-start] auto [heading-end]
40px [text-start] auto [media-end]
1fr [text-end]<span class="token punctuation">;</span>
<span class="token property">gap</span><span class="token punctuation">:</span> 0 20px<span class="token punctuation">;</span>
<span class="token punctuation">}</span></code></pre>
<p>Here’s the full demo:</p>
<iframe height="474" scrolling="no" title="CSS Grid Complex Component" src="https://codepen.io/michellebarker/embed/yGzWme/?height=474&theme-id=0&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;">See the Pen <a href="https://codepen.io/michellebarker/pen/yGzWme/">CSS Grid Complex Component</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>This solution was not actually implemented in production – it was only much later that I figured this out! Although it’s not a common layout problem to have, I hope this demonstrates some of the tricks and methods that can be employed with Grid to produce unusual layouts.</p>
Wrapping Up 20182018-12-27T00:00:00Zhttps://css-irl.info/wrapping-up-2018/<p>The past few days I’ve been reading a lot of people’s end-of-year reviews, where they share their personal and professional accomplishments, and their hopes and goals for the year ahead. I wasn’t going to do one of these, but reading all the others got me feeling inspired, so here we are!</p>
<p>This being (primarily) a CSS blog, I’m going to keep most of this based around CSS, or at least front end development. But it’s been an interesting year of accomplishments for me personally too, so forgive me if I occasionally deviate into the personal. So let’s kick things off...</p>
<h2>What’s new in 2018</h2>
<h3>CSS {In Real Life} was born!</h3>
<p>One of my personal goals for 2018 was to do more writing. Writing about web technologies is something I used to do quite a bit of a few years ago while working as a designer and event producer. Switching to front end development full time at <a href="https://ournameismud.co.uk/">Mud</a> in 2016, during a time of huge upheaval in my personal life, meant writing was put on the backburner for a while. It’s something I really missed. I had (and still have) so many ideas for things I wanted to write about, I decided 2018 was the year to make it happen.</p>
<p>CSS Grid has interested me for quite a long time – ever since attending <a href="https://rachelandrew.co.uk/">Rachel Andrew</a>’s workshop in 2015. (Before that I was geeking out about <a href="https://www.smashingmagazine.com/2015/07/smarter-grids-with-sass-and-susy/">other kinds of grids too</a>.) Earlier this year I became interested in CSS variables and how they could be used in combination with Grid to help manage complex layouts, as a result of a project I worked on at Mud. I wrote up a <em>really</em> quick article (I wrote most of it on the 20-minute train journey home), built a shaky proof-of-concept, and published it as a <a href="https://codepen.io/michellebarker/post/super-powered-layouts-with-css-variables-css-gr">blog post on Codepen</a>. That post ended up being more popular than I could have imagined, and was widely shared (over 22,000 views at last count!). That gave me a massive boost, encouraging me to start this blog and keep writing regularly.</p>
<h3>Super-powered layouts</h3>
<p>2018 was the year of CSS Grid, CSS variables and super-powered layouts – at least for me. It was the year we really started to see Grid being used in production, and widely talked and written about. <a href="http://jensimmons.com/">Jen Simmons</a> is one of the main people leading the way on this. This year she launched a YouTube channel, <a href="https://www.youtube.com/channel/UC7TizprGknbDalbHplROtag">Layout Land</a>, and heralded the dawning of a new era of web layout by coining the term <em>Intrinsic Web Design</em> to describe the next evolutionary step forward, beyond responsive design. This episode of the <a href="http://www.zeldman.com/2018/05/02/transcript-intrinsic-web-design-with-jen-simmons-the-big-web-show/">Big Web Show</a> is well worth a listen to get fully up to speed on what all this means for web layout.</p>
<h3>Gatsby</h3>
<p><a href="https://www.gatsbyjs.org/">Gatsby</a> is a static site generator built with React – it’s what powers this blog, in fact. A couple of colleagues at Mud were playing around with it early this year and decided to give it a whirl. I’ve written <a href="https://css-irl.info/introduction-to-gatsby/">a post</a> about it, so I won’t go into it all here, but basically it’s very fast, relatively easy for newcomers to get to grips with (you don’t even need previous knowledge of React – thanks to the excellent docs), and a bit of a gamechanger in the static site gen world.</p>
<p>It seems like other static site generators are emerging and/or upping their game this year. Although I can recommend Gatsby, I haven’t used others, so don’t have much of a reference point. I’d love to hear of any you can recommend and why!</p>
<h3>Firefox</h3>
<p>I switched to Firefox as my default browser around October this year. The dev tools are fantastic and getting better all the time. There are now inspectors for CSS Grid (one of the major selling points for me!), accessibility, animation and more. MDN docs are the go-to for almost every developer I know, so to me it makes sense to support Mozilla. Not to mention being able to reclaim a little bit of my soul from Google, by no longer using Chrome.</p>
<p>With the recent news that Microsoft Edge will use the Chromium rendering engine in future, there’s all the more reason to support a truly independent browser and resist a monopoly. Jeremy Keith urges us all to <a href="https://adactio.com/journal/14608">get behind Firefox</a>.</p>
<h3>Tailwind</h3>
<p>Earlier this year the Mud team decided to adopt utility-first CSS – specifically the framework <a href="https://tailwindcss.com/">Tailwind</a>. After six months or so of using it, I can definitely see the benefits, and on the whole it was the right approach for us to adopt as a team. That’s not to say I’m an advocate for a utility-first approach in every situation, and there are certain drawbacks to counteract the positives. I’ll share my thoughts in a post in the next few weeks. In the meantime, here’s a <a href="https://frontstuff.io/in-defense-of-utility-first-css">great article by Sarah Dayan</a> that breaks down why utility-first CSS is worth considering.</p>
<h3>Personal achievements in 2018</h3>
<p>It’s been an exciting year for me personally. I spoke at four events, and published 20 articles, including two articles for <a href="https://css-tricks.com/">CSS Tricks</a> – a major life goal! In November the writing took a bit of a dip as I spent the month learning React. (I took Wes Bos’s <a href="https://reactforbeginners.com/">React for Beginners</a> course, which I thoroughly recommend.) In December I started a new job at <a href="https://ordoo.co.uk/">Ordoo</a>. I’m really excited to have the chance to work on a product and have a lot of creative input, as well as putting my new found React knowledge into practice!</p>
<h3>What’s not so great in 2018</h3>
<p>Of course, it’s hard to talk about 2018 without thinking about the massive political shitstorm (there really is no other word for it) going on on both sides of the Atlantic. I don’t have much to say here that hasn’t already been said, and by far more articulate people than me. As much as I love working on the web, it can feel like what we’re doing is insignificant compared to the terrible, terrible things in this world. But on the other hand, it makes me feel like creativity, conversation and learning for learning’s sake are more important now than ever.</p>
<h2>Looking ahead to 2019</h2>
<p>Now we’re onto the good part! These are the new CSS specifications that I’m really excited about – hopefully coming to a browser near you in 2019!</p>
<h3>Aspect ratio</h3>
<p>We’ll soon be able to define aspect ratio on an image (or indeed on anything!) in CSS! No more padding hacks! This is part of the <a href="https://drafts.csswg.org/css-sizing-4/#ratios">Intrinsic and Extrinsic Sizing Module Level 4</a>, and is still in rough draft. Is it too much to hope it’ll land in 2019? Maybe. But I’ll stay hopeful!</p>
<h3>Subgrid</h3>
<p>Subgrid (where the children inherit the grid of the parent container) is the main new feature in the <a href="https://www.w3.org/TR/css-grid-2/">CSS Grid Layout Module Level 2</a> spec. It’s probably one of the most-requested features in Grid, and is going to make Grid an even more practical choice for layouts. Rachel Andrew has an excellent <a href="https://www.smashingmagazine.com/2018/07/css-grid-2/">breakdown of what it means</a>. The spec is in Working Draft, but we shouldn’t have too long to wait!</p>
<h3>Environment variables</h3>
<p>In a nutshell, CSS environment variables are like global variables. Ire Aderinokun summarises them nicely in <a href="https://bitsofco.de/css-environment-variables/">this article</a>. <a href="https://drafts.csswg.org/css-env-1/">The spec</a> is in Editor’s Draft and will probably change – but some browsers are already supporting it, meaning you can play around if you’re curious.</p>
<h2>Goals for this blog</h2>
<p>In 2019 I want to keep consistently writing on this blog, hopefully publishing fortnightly at a minimum. One of the things I had always intended to do with this blog is to share shorter and more frequent posts and code snippets from the projects I’m working on in everyday life. That didn’t end up happening, as every time I started writing an article I ended up going down a rabbit hole and writing several hundred more words than I planned to!</p>
<p>Blogging was always meant to be an enjoyable thing for me, so I don’t want to stress about it if life sometimes gets in the way. Hopefully I’ll keep writing and people will keep reading :)</p>
<p>I do want to make some improvements: Adding pagination, tagging and search functionality all feel pretty vital as the number of posts grows. So look out for some enhancements over the next few months.</p>
<p>I also want to start adding some better illustrations into my blog posts. I studied illustration for my degree, and used to do quite a bit of freelance illustration work, which has been put aside for a while. I’d like to weave that into my posts somehow!</p>
<h2>Personal goals</h2>
<p>I’m not one for setting big, ambitious goals that I then feel under pressure to complete over the course of a year. But these are some things I’d like to do <em>at some point</em> in 2019:</p>
<h3>Get better at public speaking</h3>
<p>I spoke at four events this year, but I still don’t feel I’m great at it. The feedback I’ve had has been positive, but it doesn’t come naturally to me, and I still get nervous. Doing it has been a great confidence boost at times, and a good way to meet people.</p>
<h3>Do more drawing</h3>
<p>This one doesn’t need much explaining, but it’s something I want to make time for without having the pressure of a deadline.</p>
<h3>Spend more time with my family and less time stressing</h3>
<p>I’ve worked really hard in 2018, and while I certainly won’t be resting on my laurels, I want to give myself a bit of a break in 2019 to be healthy, and spend less time in front of the screen.</p>
<p>Happy New Year, and best wishes for a successful 2019 – whatever that looks like to you!</p>
Reversing an Easing Curve2018-12-18T00:00:00Zhttps://css-irl.info/reversing-an-easing-curve/<p>Published on CSS Tricks</p>
Into the Matrix with SVG Filters2018-11-20T00:00:00Zhttps://css-irl.info/into-the-matrix-with-svg-filters/<p>In this article we’ll explore how to use SVG filters for advanced colour manipulation on images.</p>
<h2>Blend modes and beyond</h2>
<p>If you’re writing CSS regularly there’s a good chance you will have come across <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/blend-mode">blend modes</a>. The <code>background-blend-mode</code> and <code>mix-blend-mode</code> properties allow us to blend a background and a foreground element together, and when used on images can help create some interesting effects, similar to the way image editing programs like Photoshop do. You can get really creative and even replicate Instagram’s filters, <a href="https://una.im/CSSgram/">like Una Kravets has done here</a>. You can even create <a href="https://jmperezperez.com/duotone-using-css-blend-modes/">duotone images</a> using <code>mix-blend-mode</code> on pseudo-elements (another trick from Una!).</p>
<p>CSS blend modes are not currently supported in Edge and IE11, so they’re best treated as an enhancement rather than something that should be relied upon for your site. However, sometimes they’re not quite enough on their own. Sometimes we might want a bit more control over things like blur, contrast and colour manipulation.</p>
<h2>CSS filters</h2>
<p>CSS filters give us a few more capabilites where blend modes don’t fully satisfy our needs. While blend modes depend on a second colour or image to blend with the original (whether a background image, another element of a pseudo-element), CSS filters work on the image directly. We can blur images, convert them to greyscale, add a drop-shadow, or rotate the hue, for example. There’s a <a href="https://css-tricks.com/almanac/properties/f/filter/">great introduction on CSS Tricks</a>, which explains some of their capabilities. However, for full control over our images we have SVG filters. CSS filters, while incredibly useful and a great tool to have in CSS, are a simplified implementation of SVG filters – and knowing about SVG filters can give us superpowers when it comes to image manipulation! Even better, support for SVG filters goes right back to IE10, giving them a clear advantage over CSS filters in many situations.</p>
<aside>
<h4>Edit: <time datetime="2019-05-14">14 May 2019</time></h4>
<p>At present, to support SVG filters in IE and Edge you need to use <code><image></image></code> <a href="https://stackoverflow.com/questions/22003865/svg-filter-on-html-img-in-ie10">inside the SVG</a>. Applying a filter to an external image with CSS, as in the examples here, will not work in those browsers. More information at <a href="https://caniuse.com/#search=svg%20filter">caniuse.com</a>.</p>
</aside>
<h2>FeColorMatrix</h2>
<p>SVG filters open up a whole new world of image effects, but the one I want to focus on in this article is the <code>feColorMatrix</code> filter, which allows us to manipulate the red, green, blue and alpha channels of an image by adding different amounts of red, green, blue or alpha into them. Still with me? <code>feColorMatrix</code> (<code>fe</code> stands for “Filter Effect” in SVG filters) allows for highly nuanced colour adjustment.</p>
<h3>Writing an SVG filter</h3>
<p>SVG filters can be written inline in your HTML like this:</p>
<pre><code><svg viewBox="0 0 600 400" width="0" height="0" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<filter id="myFilter">
<!--the rest of the code for the filter goes here-->
</filter>
</defs>
</svg>
</code></pre>
<p>Notice we’re wrapping the filter in a <code><defs></code> tag so it becomes a symbol we can reuse. I’ve giving the SVG a width and height of <code>0</code> so it doesn’t show up on the page – it’s purely for defining the filter.</p>
<p>The filter needs an <code>id</code>, which we can reference in our CSS to apply a filter, like this:</p>
<pre><code>.fig {
filter: url(#myFilter);
}
</code></pre>
<p>Here’s an example of an <code>feColorMatrix</code> filter:</p>
<pre><code><filter id="myFilter">
<feColorMatrix in="SourceGraphic"
type="matrix"
values="1 1 0 0 0
1 1 0 0 0
1 1 0 0 0
0 0 0 1 0" />
</filter>
</code></pre>
<p>The syntax looks quite complicated at first glance, but it can be helpful to visualise it like this:</p>
<figure>
<img src="https://css-irl.info/svg-filters_01b-01.png" alt="Colour matrix grid" />
</figure>
<p>The <em>x</em> axis represents the channels of our original image (red, green, blue and alpha), and the <em>y</em> axis represent the colours we can add or remove from those channels. The final value on the <em>x</em> axis is the multiplication factor, which we won’t worry too much about for now.</p>
<p>The matrix for a regular (unedited) image looks like the one above. The red, green, blue and alpha values are all in their original channels - so the red pixels will be red, the green pixels will be green, and so on.</p>
<p>To colourize images we can introduce different amounts of red, green or blue into other channels. For example, we can add blue to each channel to create a blue colorized image:</p>
<p>We can turn a colour image greyscale by removing red, green and blue from all channels except one:</p>
<figure>
<img src="https://css-irl.info/svg-filter_02-01.png" alt="Colour matrix grid with values only in red channel" />
</figure>
<p>This demo shows a number of different combinations we could use to get a greyscale image. Adjusting the values in the alpha channel can give us greater degrees of contrast, darkening or lightening the image.</p>
<iframe height="365" scrolling="no" title="SVG filter greyscale" src="https://codepen.io/michellebarker/embed/RqZqQJ/?height=265&theme-id=0&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;">See the Pen <a href="https://codepen.io/michellebarker/pen/RqZqQJ/">SVG filter greyscale</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>In the following matrix we’re setting all the values in the red channel to 100% (giving us a greyscale image), then adding blue into the blue channel – so the image has a blue-ish tinge:</p>
<figure>
<img src="https://css-irl.info/svg-filters_03-01.png" alt="Colour matrix grid with all values in the red channel at 1, and the blue value in the blue channel at 1" />
</figure>
<p>To create a duotone effect (with better browser support than blend modes!) we need to apply values to the alpha channel. The alpha channel can darken and lighten the image. To darken parts of the image we can use negative values, while positive values will lighten it. (Setting all values in the alpha channel to <code>1</code> will make the image completely white.)</p>
<pre><code><filter id="myFilter">
<feColorMatrix in="SourceGraphic"
type="matrix"
values="1 1 1 0 0
0 0 0 -0.5 0
0 0 0 0.2 0
0 0 0 1 0" />
</filter>
</code></pre>
<p>This gives us a duotone effect like this:</p>
<figure>
<img src="https://css-irl.info/svg-filters_duotone.png" alt="Duotone image" />
</figure>
<p>It can be a little trickier to find the perfect mix of colours using the alpha channel, but playing around with the values will give you a feel for it. Hopefully this introduction has given you a taste of what’s possible.</p>
<p>To learn more about how <code>feColorMatrix</code> works here are two articles that explain it really well:</p>
<ul>
<li><a href="https://alistapart.com/article/finessing-fecolormatrix">Finessing <code>feColorMatrix</code></a> by Una Kravets</li>
<li><a href="https://css-tricks.com/color-filters-can-turn-your-gray-skies-blue/">CSS Filters Can Turn Your Gray Skies Blue</a> by Amelia Bellamy-Royds</li>
</ul>
<p>The best way to really understand <code>feColorMatrix</code> is to play around with the values yourself.</p>
<p>Here is a demo showing a few of the creative possibilities:</p>
<iframe height="365" scrolling="no" title="SVG filter feColorMatrix" src="https://codepen.io/michellebarker/embed/mQBRRX/?height=265&theme-id=0&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;">See the Pen <a href="https://codepen.io/michellebarker/pen/mQBRRX/">SVG filter feColorMatrix</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
Mentoring Junior Developers2018-10-28T00:00:00Zhttps://css-irl.info/mentoring-junior-developers/<p>Last week I gave a talk at <a href="https://bathdigitalfestival.co.uk/">Bath Digital Festival</a> about <a href="https://noti.st/mbarker84/YhiZtb/super-powered-layouts-with-css-grid-and-css-variables">CSS Grid and CSS Variables</a>. I was lucky enough to be able to attend a full day of talks and saw some really interesting sessions on topics ranging from developing with components (Jack Franklin), to using data as a creative material (Mike Brondbjerg), to machine learning (Simona Cotin). But one talk really stood out for me, as it’s a subject that’s close to my heart, and that’s <a href="https://twitter.com/tara_ojo">Tara Ojo</a>’s talk on mentoring junior developers.</p>
<p>I started as a junior front end developer at <a href="https://ournameismud.co.uk/">Mud</a> just three years ago, and many of the emotions of that time are still fresh in my mind. Being a junior is undoubtedly a position that (almost) every developer has been in at one point or another, and yet I feel that we as an industry could certainly do more to foster empathy and cultivate new talent.</p>
<p>I can’t speak for everyone, but for me the experience of being junior developer was exhilharating but intense, and at times overwhelming. I felt (and still feel) like there was so much to learn. Part and parcel with a career in front end development is accepting that that feeling never truly goes away – there will always be more to learn – and even embracing it as part of what makes our industry so interesting to work in. But it does mean it’s incredibly important to receive encouragement and validation from the people you’re working with – otherwise it’s all to easy to feel like you’re not progressing, even though it’s likely that you’ve learnt a huge amount in your period as a junior developer.</p>
<p>One of Tara’s key takeaways for those mentoring junior developers is to give them feedback about their work, and tell them when they’ve done something well, as well as things they could work on. It might sound obvious, but in practice I don’t think it happens nearly often enough. When you’re up against multiple deadlines, it can be hard to find the time to monitor and review the work of junior developers. But I think it’s important to make the time for this. In my experience, getting constructive feedback and praise when things have gone well makes so much difference to how someone will approach their work going forward. Are people more likely to do their best work and go above and beyond when they’re feeling happy and secure in their jobs? I would bet money on it.</p>
<p>In my opinion, if you make someone feel like they can do anything they set their mind to, and give them the time, encouragement and tools to reach their goals, they will often rise to the challenge.</p>
<p>Tara also gave some practical tips for ways to help juniors measure and improve their performance, including some charts and diagrams that could be used to collaboratively track progress and focus learning opportunities. These charts would be revisited at each review point (whether this is annually, or more regularly) and, crucially, the person being reviewed would have the opportunity to evaluate their own progress and see how far they had come since the last time. Reminding yourself of the progress you’ve made is great confidence boost.</p>
<p>In her talk, Tara had some great tips for ways that junior developers could help themselves too. These can be summed up in three points:</p>
<ol>
<li>Work on your hard skills – by this she means your coding skills, which are of course what you’re being paid to do, and the most important skills for your job. Other stuff is important too, but improving your code is the way you’ll really get ahead. That means not only writing code that works, but writing <em>quality</em> code, that works well for your team.</li>
<li>Share your opinions – this can sometimes be tough when you’re a junior developer as it feels like everyone knows more than you. But you have a valid voice, and can also bring a fresh perspective.</li>
<li>Celebrate your acheivements – I’m a big fan of this one. Remind yourself of your successes, whether big or small, and give yourself a reward. Tara keeps a Trello board of her personal acheivements, and I’m totally stealing this idea!</li>
</ol>
<p>There’s a whole lot more to Tara’s talk, so if you’re interested in learning more about how you can support junior developers – and if you’re a junior developer, how you can develop yourself – check out her slides here:</p>
<iframe src="https://www.slideshare.net/slideshow/embed_code/key/Z5FnGYVHhrUYj" width="100%" height="485" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" style="border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%;" allowfullscreen=""> </iframe> <div style="margin-bottom:5px"> <strong> <a href="https://www.slideshare.net/TaraOjo/juniornextbathdigitalfestival" title="Junior.next(BathDigitalFestival)" target="_blank">Junior.next(BathDigitalFestival)</a> </strong> from <strong><a href="https://www.slideshare.net/TaraOjo" target="_blank">Tara Ojo</a></strong> </div>
Negative Grid Lines2018-10-14T00:00:00Zhttps://css-irl.info/negative-grid-lines/<p>Did you know you can use negative line numbers to position grid items with CSS Grid? I didn’t until recently – or rather, I hadn’t given it any thought, as I never felt like I needed to before.</p>
<p>But it stuck me recently, after writing <a href="https://css-irl.info/relative-grid-tracks">this article on relative positioning of grid items</a>, that negative lines could be advantageous here.</p>
<p>If you place an grid item using a line number with a positive value, if that line doesn’t exist yet (because there aren’t enough tracks in your grid) then <em>implicit</em> tracks will be created.</p>
<figure>
<img src="https://css-irl.info/implicit-tracks.jpg" alt="Showing two implicit tracks created on the column axis" />
<figcaption>Implicit tracks created on the column axis by placing an item outside of the explicitly defined grid</figcaption>
</figure>
<p>This is useful because we don’t need to define a precise number of tracks if we don’t know how many grid items we’ll be placing. An example might be a news feed or image gallery with dynamic content. We can control the size of these implicit tracks using the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-columns"><code>grid-auto-columns</code></a> and <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/grid-auto-rows"><code>grid-auto-rows</code></a> properties.</p>
<p>What I expected to happen when placing items with negative line numbers was that implicit tracks would be created in the reverse direction - this would be the tracks above the explicitly defined grid tracks on the row axis, or to the left on the column axis (if using the default left-to-right writing mode, which defines the grid content flow). What <em>actually</em> happens is more interesting, and probably a lot more useful.</p>
<p>Imagine I have a grid defined like so, with four explicit tracks on each axis:</p>
<pre><code>.grid {
grid-template-columns: repeat(4, 1fr);
grid-template-rows: repeat(4, 200px);
}
</code></pre>
<p>I can place an item like this (using the <code>grid-column</code> shorthand property for <code>grid-column-start</code> / <code>grid-column-end</code>):</p>
<pre><code>.item {
grid-column: -1 / span 3;
}
</code></pre>
<p>Rather than being starting two grid lines <em>before</em> grid line 1, the item is placed at the very last grid line.</p>
<figure>
<img src="https://css-irl.info/negative-lines.jpg" alt="An item placed starting at grid line -1" />
<figcaption>Item placed starting at grid line -1, generating implicit tracks</figcaption>
</figure>
<p>In this example I’m using the Grid inspector in Firefox dev tools, which, handily, shows negative grid line numbers as well as positive ones. If I place the item starting at grid-line -2, it will start one grid line from the last one, -3 will be 2 grid lines from last, and so on.</p>
<p>This grid has four tracks, so five grid lines if we ignore any implicit tracks. But we could feasibly place the item using a negative number larger that the ones available, which would create implicit tracks in the other direction:</p>
<pre><code>.item {
grid-column: -8 / span 3;
}
</code></pre>
<figure>
<img src="https://css-irl.info/negative-lines2.jpg" alt="An item placed starting at grid line -8" />
<figcaption>Item placed starting at grid line -8, generating implicit tracks to the left</figcaption>
</figure>
<p>Note, as we’re creating implicit tracks to the left, the first grid item is now placed at the first implicit track, rather than on the explicit grid. This is because I haven’t explicitly placed the other grid items, so they are being auto-placed into the first available cells.</p>
<h3>Why is this useful?</h3>
<p>Using negative grid lines allows us to place items relative to the end of the grid. This is especially useful if we have a large grid – it means we don’t have to work out the exact line number from the start, we could simply place it from the end. In a 24 column grid, for example, we might place an item using an end line like this:</p>
<pre><code>.item {
grid-column: span 8 / 24;
}
</code></pre>
<p>This requires us to remember that the 24th line is one line away from the end. But perhaps the following is a bit more intuitive:</p>
<pre><code>.item {
grid-column: span 8 / -2;
}
</code></pre>
<p>This could be especially useful when it comes to centering items on a grid. If we know that an item needs to be an equal number of tracks from the start and the end of the grid, then we only need to set the end line as the negative equivalent of the start line:</p>
<pre><code>.item {
grid-column: span 2 / -2;
}
</code></pre>
<p>If we need to increase the size of our grid at different breakpoints but our grid item still needs to be centered, placing it with negative lines means we don’t necessarily need to place the item again. For example, in this 12-column grid, which becomes a 16-column grid at larger breakpoints, the item I’m placing on the grid still starts and end the same number of tracks from the grid edges, no matter how many columns there are:</p>
<pre><code>.grid {
grid-template-columns: repeat(12, 1fr);
}
.item {
grid-column: span 2 / -2;
}
@media (min-width: 60em) {
.grid {
grid-template-columns: repeat(16, 1fr);
}
}
</code></pre>
<p>Here’s a full demo where several items are placed using negative line numbers:</p>
<iframe height="365" scrolling="no" title="Layout with negative grid line numbers" src="https://codepen.io/michellebarker/embed/ReLYwp/?height=265&theme-id=0&default-tab=result" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;">See the Pen <a href="https://codepen.io/michellebarker/pen/ReLYwp/">Layout with negative grid line numbers</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<h3>Bonus</h3>
<p>This technique for centering items also lends itself well to working with CSS variables. We can set the start line as a variable and the end line is calculated as it’s equivalent negative value:</p>
<pre><code>.item {
--start: 4;
--end: calc(var(--start) * -1);
grid-column: var(--start) / var(--end);
}
</code></pre>
<p>If we do need to change the start line and end line at certain breakpoints, all we need to do is update one value. Nice!</p>
20 Inspiring Women in Tech2018-09-25T00:00:00Zhttps://css-irl.info/20-inspiring-women/<p><a href="https://medium.com/@markobijelic">Marko Bijelic</a>, designer at <a href="https://hipinspire.com/">Hipinspire</a>, recently published <a href="https://medium.com/hipinspire/web-design-legends-where-are-they-now-becfa4f481ff">this article on Medium</a> title <em>Web design legends: where are they now?</em>. The article was a look back at <a href="https://cameronmoll.carrd.co/">Cameron Moll</a>’s who’s who of web design in 2004, exploring the 20 individuals’ influence on the web today. While it was interesting to read Marko’s post and reflect on the undeniable influence of many of these leading figures, it was impossibly to notice the distinct absence of women from the original list. In fact, only one woman (<a href="https://twitter.com/dahnahdee">Donna Driscoll</a>) gets a mention, and in Marko’s article she is only referred to with a footnote in her ex-husband’s entry:</p>
<blockquote>
<p>Donna is his ex-wife.</p>
</blockquote>
<p>To the author’s credit, he does suggest two women who should have made the cut. It’s hard to imagine such a male-heavy list being published now, not least because of the sheer number of incredible women at the forefront of web design and development. But it’s a reminder of how hard it must have been to those starting their careers all those years ago, with very few visible role models to look up to and to lean on.</p>
<p>To further redress the balance, here in no particular order are 20 (in my opinion) current web design (and development) legends who happen to identify as female. I’m forever grateful to them for blazing a trail and inspiring me in my work. I look forward to reflecting on their incredible careers in 15 years time!</p>
<ol>
<li>###<a href="https://sarahdrasnerdesign.com/">Sarah Drasner</a>
Incredibly skilled developer, writer and speaker on SVG, Vue, web animation and much more</li>
<li>###<a href="https://www.sarasoueidan.com/">Sara Soueidan</a>
CSS and accessibility-focused front end developer, author and speaker. I cannot emphasise enough how much I’ve learned from Sara’s articles!</li>
<li>###<a href="https://una.im/">Una Kravets</a>
Director of Product Design at Bustle, also known for her great articles, podcast and fun CSS experiments</li>
<li>###<a href="http://jensimmons.com/">Jen Simmons</a>
Designer Advocate at Mozilla, web layout and CSS Grid pioneer and creator of Layout Land YouTube channel</li>
<li>###<a href="https://rachelandrew.co.uk/">Rachel Andrew</a>
The other person integral to the current CSS Grid revolution, as well as being a developer, writer and editor in chief at Smashing Magazine</li>
<li>###<a href="https://twitter.com/stubbornella">Nicole Sullivan</a>
Literally invented OOCSS, one of the early methodologies for scaling CSS</li>
<li>###<a href="https://www.sushiandrobots.com/">Jina Bolton</a>
Jina has been banging the drum for Design Systems since before they were cool, and runs Clarity, a design systems conference</li>
<li>###<a href="http://jessicahische.is/">Jessica Hische</a>
Designer, typographer and hand-letterer extraordinaire</li>
<li>###<a href="http://darngood.co/">Meg Lewis</a>
Multi-discipliary designer and quarter of NYC studio Ghostly Ferns, with a bold, fresh style</li>
<li>###<a href="http://mina.codes/">Mina Markham</a>
Creative front end developer and design systems advocate, including for Hillary Clinton’s campaign</li>
<li>###<a href="https://valhead.com/">Val Head</a>
Web animation expert, speaker and author, currently at Adobe</li>
<li>###<a href="https://ireaderinokun.com/">Ire Aderinokun</a>
UI designer and front end developer who also writes super useful articles on her blog <a href="https://bitsofco.de/">bitsofcode</a></li>
<li>###<a href="http://rachelnabors.com/">Rachel Nabors</a>
Award-winning cartoonist and specialist in web animation</li>
<li>###<a href="https://larahogan.me/">Lara Hogan</a>
Engineering and leadership coach</li>
<li>###<a href="https://twitter.com/saronyitbarek">Saron Yitbarek</a>
Founder of the <a href="https://www.codenewbie.org/">Codenewbie</a> community</li>
<li>###<a href="https://twitter.com/anatudor">Ana Tudor</a>
Builder of incredible, creative, UI components that showcase the incredible power of CSS and Sass</li>
<li>###<a href="https://www.maban.co.uk/">Anna Debanham</a>
Product manager, and early advocate for front end style guides</li>
<li>###<a href="https://tink.uk/">Leonie Watson</a>
Web standards and accessibility expert, and W3C board member</li>
<li>###<a href="https://laurakalbag.com/">Laura Kalbag</a>
Designer, accessibility advocate and author of <em>Accessibility for Everyone</em>, published by <em>A Book Apart</em>.</li>
<li>###<a href="http://lea.verou.me/">Lea Verou</a>
No ‘who’s who’ would be complete without Lea Verou, web standards advocate and CSS legend</li>
</ol>
Relative Grid Items with CSS Variables2018-09-13T00:00:00Zhttps://css-irl.info/relative-grid-tracks/<p>I was helping my colleague get started with a CSS Grid layout recently and he asked me if there was a way of offsetting grid items relative to one another, e.g “If column 1 ends at line <em>x</em>, make column 2 start at line <em>x + n</em>”. There isn’t exactly a way of doing this natively with Grid, you would need to explicitly position the items in this case. But it occurred to me that this could be a use case for CSS variables, or custom properties.</p>
<p>Previously I have compared the difference between CSS variables and Sass (or other preprocessor) variables as similar to the difference between <code>const</code> and <code>let</code> in JavaScript. CSS variables are more like <code>let</code> as they are dynamic, not static. But that analogy is a little misleading, as there is a crucial difference: With CSS variables there is no way of accessing a locally scoped variable outside of its scope.</p>
<p>An example:</p>
<pre><code>.my-component {
--bgColor: red;
}
/* This will not work, the variable is undefined */
.another-component {
background-color: var(--bgColor);
}
</code></pre>
<p>Therefore, if we want to position the items on our grid layout relative to one another, we need to define those variables on the parent grid container so that they will be inherited by the children.</p>
<pre><code>.grid {
--start1: 1;
--end1: 4;
--start2: calc(var(--end1) + 2);
}
.grid__item:first-child {
grid-column: var(--start1) / var(--end1);
}
.grid__item:nth-child(2) {
grid-column: var(--start2) / 12;
}
</code></pre>
<p>The start line of the second <code>.grid__item</code> will always be two grid lines after the end line of the first item. If we have a second variant of our <code>grid</code> component where we want to change the position of the first <code>.grid__item</code> then the start of the second grid child will be updated accordingly:</p>
<pre><code>.grid—2 {
--end1: 6:
}
</code></pre>
<p>Here’s a demo of the concept in action:</p>
<iframe height="365" scrolling="no" title="Relative columns with CSS variables" src="https://codepen.io/michellebarker/embed/xajVqz/?height=352&theme-id=0&default-tab=result&embed-version=2" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;">See the Pen <a href="https://codepen.io/michellebarker/pen/xajVqz/">Relative columns with CSS variables</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>One caveat here is that we need to watch that the position of our first grid item doesn’t cause the second item to be positioned beyond the total number of columns specified in our <code>grid-template-columns</code> property. In this particular case I’m hard-coding the item’s end line, so that’s not going to be an issue. But the first item could still push its start line over our column count, so that’s something to watch out for. Variables could be super powerful with <code>if</code> statements, but as yet we don’t have that capability in CSS!</p>
<p>We don’t need to stick to just two items either – in theory, we could have many items that are positioned relative to each other. Here’s a slightly more complex demo, where we’re placing items relative to one another on the column <em>and</em> row axis:</p>
<iframe height="365" scrolling="no" title="Relative grid items with CSS variables" src="https://codepen.io/michellebarker/embed/PdaBvj/?height=265&theme-id=0&default-tab=result&embed-version=2" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;">See the Pen <a href="https://codepen.io/michellebarker/pen/PdaBvj/">Relative grid items with CSS variables</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>It’s possibly to do a similar thing with Sass variables, but the advantage with CSS variables is the reusability of our code for different variants of the same component. If we had multiple variants then we’d only need to update a few variables, rather than writing out the grid declaration all over again.</p>
<p>There are definitely some possibilities here, even though I haven’t worked on any real-world use cases yet where this approach would be preferable to manually placing items at different grid lines. I’d be interested to see if this method of placement works out for anyone, so if you try it out do let me know!</p>
Notes from State of the Browser2018-09-11T00:00:00Zhttps://css-irl.info/notes-from-state-of-the-browser/<figure>
<img src="https://css-irl.info/sotb01.jpg" alt="Charlie Owen on stage with slide: You don’t work for Facebook" />
<figcaption>Charlie Owen at State of the Browser</figcaption>
</figure>
<p>I recently had the pleasure of speaking at <a href="https://2018.stateofthebrowser.com/">State of the Browser</a>, a community-run web conference in London. I gave my first technical talk on a large stage, speaking about CSS Grid and CSS Variables – some of which I’ve covered in my blog posts over the past few months. My slides can be found <a href="https://noti.st/mbarker84/GzviRt/super-powered-layouts-with-css-grid-and-css-variables">here</a>.</p>
<p><a href="https://noti.st/mbarker84/GzviRt/super-powered-layouts-with-css-grid-and-css-variables"><img src="https://css-irl.info/state-of-the-browser.jpg" alt="Super-powered Layouts with CSS Grid and CSS Variables" /></a></p>
<p>The event featured some fantastic talks – some of my favourites included <a href="https://2018.stateofthebrowser.com/speakers/ruth-john/">Ruth John</a>’s audio-visual exploration of web-workers and <a href="https://2018.stateofthebrowser.com/speakers/chris-mills/">Chris Mills</a> showcasing Firefox’s latest developer tools. Indeed, Firefox got a lot of love from several speakers throughout the day (myself included – it’s Grid inspector makes working with CSS Grid even better!), and it might just become the developer’s primary browser of choice in the not-too-distant future.</p>
<p>I learnt something new with every talk, which you can’t always say about a conference! But the highlights for me were the two final talks of the day by <a href="https://2018.stateofthebrowser.com/speakers/jeremy-keith/">Jeremy Keith</a> (<em>The Web Is Agreement</em>) and <a href="https://2018.stateofthebrowser.com/speakers/charlie-owen/">Charlie Owen</a> (<em>Dear Developer</em>). In light of the recent arguments going on online about the state of CSS and Javascript, these two talks felt exceptionally timely, and a reminder that the web’s purpose transcends all of us.</p>
Super-powered Grid Components with CSS Custom Properties2018-08-28T00:00:00Zhttps://css-irl.info/super-powered-grid-components/<p>Published on CSS Tricks</p>
Experimental Layouts with CSS Shapes and Clip-path2018-08-14T00:00:00Zhttps://css-irl.info/experimental-layouts/<p>Recently there has been a proliferation of demos using the CSS property <code>shape-outside</code> to create interesting and unusual text and layout effects. (<a href="https://codepen.io/mandymichael/pen/xzyrGa">Here’s one example</a> by Mandy Michael)</p>
<p>The <code>shape-outside</code> property (part of the <a href="https://www.w3.org/TR/css-shapes-1/">CSS Shapes module</a> ) allows us to wrap text around the shape of an object. The object that you want the text to wrap around must have a width and height, and be floated to the left or right. Here is the simplest code we need in order to do this:</p>
<pre><code>.shape {
width: 200px;
height: 200px;
float: left;
shape-outside: circle(50%);
}
</code></pre>
<p>This will wrap a paragraph of text around a circular area like so:</p>
<p><img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/85648/Screen%20Shot%202018-08-13%20at%2021.21.53.png" alt="Text wrapping around circular shape" /></p>
<p>Here the text will wrap around in a circular shape. You can create more complex shapes using polygon():</p>
<pre><code>.shape {
width: 200px;
height: 350px;
float: left;
shape-outside: polygon(0 0, 50% 0%, 90% 50%, 50% 100%, 0 100%);
}
</code></pre>
<p>Here I’m “drawing” a polygon using pairs of x and y co-ordinates. Currently, while the text will wrap around the shape, the <code>.shape</code> object itself will still be a square. If we give it a background colour you can see what I mean:</p>
<p><img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/85648/Screen%20Shot%202018-08-13%20at%2021.32.40.png" alt="Text wrapping around a polygon" /></p>
<p>What if we want the shape to have a background colour <em>and</em> for the text to wrap around it? If we want the object to be circular we can of course give it a <code>border-radius</code> of 50%. But what if we want it to follow the same path as the <code>shape-outside</code> property when we’re using a more complex shape? In that case we can use <code>clip-path</code>, and give it the same value as our <code>shape-outside</code> property. We can also use the <code>shape-margin</code> property to add a bit of distance between the shape and the content wrapping around it:</p>
<pre><code>.shape {
width: 200px;
height: 350px;
background-color: #EC407A;
float: left;
shape-outside: polygon(0 0, 50% 0%, 90% 50%, 50% 100%, 0 100%);
clip-path: polygon(0 0, 50% 0%, 90% 50%, 50% 100%, 0 100%);
shape-margin: 10px;
}
</code></pre>
<p>Here’s the full demo:</p>
<iframe height="400" scrolling="no" title="Basic CSS Shapes with clip-path() example" src="https://codepen.io/michellebarker/embed/WKWeqb/?height=265&theme-id=0&default-tab=result&embed-version=2" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;">See the Pen <a href="https://codepen.io/michellebarker/pen/WKWeqb/">Basic CSS Shapes with clip-path() example</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<h2>Inverting Shapes</h2>
<p>Things get a little bit more complicated if we want to clip the background of the text instead. Now we need to invert the clip path – that is to say, we need to create a path of the shape that would be left over if you cut the original path out of a rectangle. This takes a little bit of maths, but we can create some interesting effects. Needless to say, the more complicated your polygon, the greater the difficulty in creating an “inverted” version! I find it helps to draw it out on paper and note down the co-ordinates for each point in the polygon.</p>
<p>Here’s a demo with an inverted clip-path:</p>
<iframe height="400" scrolling="no" title="CSS Shapes and inverse clip-path" src="https://codepen.io/michellebarker/embed/bjJGLe/?height=265&theme-id=0&default-tab=result&embed-version=2" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;">See the Pen <a href="https://codepen.io/michellebarker/pen/bjJGLe/">CSS Shapes and inverse clip-path</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>Note, you can use a mixture of pixel units and percentages in a <code>clip-path</code> polygon. I’m using pixels for the horizontal dimensions as it needs to be a fixed width in order to correspond correctly to the shape path, but percentages for the vertical units in case I want to adjust the height later – that way I won’t need to recalculate all the vertical co-ordinates.</p>
<p>This gives the impression that the text is <em>inside</em> the shape, but there are a few limitations:</p>
<ol>
<li>
<p>We’re currently only clipping on one side, so our shape isn’t very interesting yet.</p>
</li>
<li>
<p>If we want to add a <code>box-shadow</code> to the shape it won’t be visible as it’s being clipped – unless the <code>box-shadow</code> has a value of <code>inset</code>. However, the shadow will be on the outer element, not the part clipped away from it.</p>
</li>
</ol>
<p>I’m going to address both of those issues one by one.</p>
<h2>Shape-inside</h2>
<p>A few years ago <a href="https://www.sarasoueidan.com/">Sara Soueidan</a> wrote a great introduction to CSS Shapes, where she also showed how to <a href="https://www.sarasoueidan.com/blog/css-shapes/">wrap text inside a shape</a> using the <code>shape-inside</code> property, which had very limited browser support at the time. Support for <code>shape-inside</code> has now been withdrawn (although is part of the Level 2 spec), which means you can’t really use it anywhere at the moment. Part of the reason for this is the withdrawal of support for CSS Regions, which would deal with any overflowing text content. However, we can create a similar effect with clever use of <code>shape-outside</code>. If I have two polygons floated to the left and right respectively then the text will fill the space in the middle of them:</p>
<pre><code>.shape {
float: left;
width: 200px;
height: 460px;
shape-outside: polygon(0 0, 100px 0, 180px 50%, 100px 100%, 0 100%);
shape-margin: 10px;
}
.shape2 {
float: right;
width: 200px;
height: 460px;
shape-outside: polygon(0 0, 100% 0, 100% 100%, 0 100%, 100px 50%);
shape-margin: 10px;
}
</code></pre>
<p>If I want to style the text background and clip it out in this case then I need to somehow work out the <code>clip-path</code> value for the space in between. I find the easiest way to do this is to think of the paths as part of the same rectangle. Then you can plot the co-ordinates for each point on the path.</p>
<p>Handily, you can use <code>calc()</code> inside a <code>clip-path()</code> fuction too, which can be useful if you’re not working with exact widths.</p>
<p>Here’s the demo:</p>
<iframe height="400" scrolling="no" title="CSS Shapes and inverse clip-path replicating shape-inside" src="https://codepen.io/michellebarker/embed/ajxzJg/?height=314&theme-id=0&default-tab=result&embed-version=2" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;">See the Pen <a href="https://codepen.io/michellebarker/pen/ajxzJg/">CSS Shapes and inverse clip-path replicating shape-inside</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>As a side-note, Bennet Feeley’s tool <a href="https://bennettfeely.com/clippy/">Clippy</a> is really useful for quickly getting clip-path co-ordinates.</p>
<h2>Creating a box-shadow effect</h2>
<p>As I mentioned, adding a box-shadow to the clipped element will have no effect. But we can create a similar effect using pseudo-elements. Moving the <code>clip-path</code> to a pseudo-element (<code>::before</code>) means we can create another absolute-positioned clipped pseudo-element (<code>::after</code>) with the same co-ordinates and apply some effects to make it feel like the element is floating above the page:</p>
<iframe height="400" scrolling="no" title="CSS Shapes and inverse clip-path and pseudo-element" src="https://codepen.io/michellebarker/embed/pZBvKQ/?height=265&theme-id=0&default-tab=result&embed-version=2" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;">See the Pen <a href="https://codepen.io/michellebarker/pen/pZBvKQ/">CSS Shapes and inverse clip-path and pseudo-element</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<h2>Using SVG</h2>
<p>We can also use images for the <code>shape-outside</code> value if the have a transparent background (SVG or PNG), which can allow us to produce some more interesting paths. To use an image you reference the URL in the <code>shape-outside</code> property value like this:</p>
<pre><code>.shape {
...
shape-outside: url(shape.svg);
}
</code></pre>
<p>Here’s a simple demo:</p>
<iframe height="400" scrolling="no" title="Shape wrapping around SVG" src="https://codepen.io/michellebarker/embed/XBQMZQ/?height=265&theme-id=0&default-tab=result&embed-version=2" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;">See the Pen <a href="https://codepen.io/michellebarker/pen/XBQMZQ/">Shape wrapping around SVG</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>I was really excited about the possibility of using a combination of SVG and clip-path, but this isn’t quite so straightforward. The difficulty here come in if you want to use the same SVG path for your clip-path value. We can use SVG for clip-path by writing the SVG inline in the markup:</p>
<pre><code><div class="shape">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 157.8 250">
<clipPath id="clip">
<path d="M140 250H0V0h140s40 36 0 125 0 125 0 125z"/>
</clipPath>
<path d="M140 250H0V0h140s40 36 0 125 0 125 0 125z"/>
</svg>
</div>
</code></pre>
<p>Then you reference the clipPath ID in your clip-path property value:</p>
<pre><code>.container {
clip-path: url(#clip);
}
</code></pre>
<p>However, this is going to clip the wrong part of the container! What I want to do here is clip the inverted part (the part that is wrapping around our shape).</p>
<p>My way of doing this is to draw the shape within a rectangle in Illustrator (or a similar vector graphics programme) so that you actually have two shapes – the original shape and its inverse.</p>
<p><img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/85648/Screen%20Shot%202018-08-14%20at%2008.23.51.png" alt="Original shape (left) and inverted shape (right)" /></p>
<p>Then you can include the clip shape within the SVG code, which will be positioned correctly within the SVG viewBox:</p>
<pre><code><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 950 250" width="0" height="0">
<clipPath id="clip">
<path d="M140 0s40 36 0 125 0 125 0 125h810V0H140z"/>
</clipPath>
</svg>
</code></pre>
<p>You may notice I’m including <code>width="0"</code> and <code>height="0"</code> – this will ensure the SVG doesn’t take up any space, as we only need to reference the <code>clipPath</code>, we don’t want it to be visible on the page.</p>
<p>In this demo I’m referencing an external SVG for the <code>shape-outside</code> property <em>and</em> the inline SVG for the <code>clip-path</code> property:</p>
<iframe height="400" scrolling="no" title="Shape wrapping around SVG with clip-path" src="https://codepen.io/michellebarker/embed/pZBPym/?height=265&theme-id=0&default-tab=result&embed-version=2" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;">See the Pen <a href="https://codepen.io/michellebarker/pen/pZBPym/">Shape wrapping around SVG with clip-path</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<h3>Simulating shape-inside with SVG</h3>
<p>Things get even more complicated if we want to clip <em>both</em> sides of the container and have the text wrap on the left <em>and</em> the right. In the demo below I’m using the same technique as the previous one, but I’m floating elements to both the left and the right. Each shape needs to be no more than 50% width, otherwise they’ll wrap below. So when I’m cutting the SVG out in Illustrator I split the original shape in half and create two SVG shapes (for the <code>shape-outside</code> property).</p>
<p><img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/85648/clip-illustration.png" alt="Left and right shapes, and their viewboxes" /></p>
<p>For each shape the <code>viewBox</code> is exactly half of the total container width, but the <code>path</code> only takes up a portion of that. This demo shows the end result, as well as utilising some of the other techniques detailed earlier in this post.</p>
<p>Be warned, as I’m using fixed sizes it only works on screen of 960px wide and above!</p>
<iframe height="400" scrolling="no" title="Simulating shape-inside with SVG and clip-path" src="https://codepen.io/michellebarker/embed/djLxvb/?height=265&theme-id=0&default-tab=result&embed-version=2" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;">See the Pen <a href="https://codepen.io/michellebarker/pen/djLxvb/">Simulating shape-inside with SVG and clip-path</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>If you want to know more about SVG, Sara Soueidan has lots of great article on <a href="https://www.sarasoueidan.com/tags/svg/">her blog</a>, where she covers things like the <code>viewBox</code> attribute and SVG co-ordinate systems in great detail.</p>
<h2>Browser support and other thoughts</h2>
<p>Browser support for CSS Shapes is around 80%, with the notable exception of Edge. Firefox’s implementation is currently behind a flag, although it should be fully implemented in the next update. Therefore a progressive enhancement approach is wise to bear in mind.</p>
<p>One thing I don’t see mentioned a lot is the UX view of Shapes. Wrapping the left edge of your text around complex shapes isn’t great for readability, and while it’s exciting to see some interesting, artistic layouts, for content that’s crucial to the user it’s important to use Shapes with the utmost caution.</p>
<p>I think that another reason this isn’t more widely used (aside from browser support) is that it can be a bit tricky to control responsively. In the last example I’m relying heavily on fixed sizes on the x and y axis to get the effect I want. If you have a text of indeterminate length the effect of wrapping text inside a shape is likely not going to work very well at different breakpoints, so for dynamic content it’s not generally a good option. That said, there’s definitely a place for experimentation when it comes to layouts, and it would be great to see a new age of web creativity flourish.</p>
Building a Progressive Web App with Gatsby2018-07-31T00:00:00Zhttps://css-irl.info/building-a-progressive-web-app/<p>In my previous article, Progressive Web Apps: An Introduction, I wrote about the key technologies involved with building a PWA and how to get started with them. In this post I’m going to tell you about my experience of building a PWA with Gatsby, a static site generator.</p>
<h2>What is Gatsby?</h2>
<p>Several members of the development team at Mud have been getting pretty excited about <a href="http://www.gatsbyjs.org/" target="_blank">Gatsby</a> recently – in fact, our recently-published, football world-cup-themed site <a href="https://ontheplane.futbol/">On the Plane</a> was built with Gatsby. Unlike other static site generators, Gatsby uses React as it’s templating engine. Everything you build is rendered in React components, making it extremely fast. I wrote about getting started with Gatsby on my personal blog, <a href="http://css-irl.info/introduction-to-gatsby/" target="_blank">here</a> (also published on the Gatsby site).</p>
<p>You don’t need to know React to get a simple Gatsby site up and running, although it’s probably helpful if you want to take advantage of everything it has to offer. However, some intermediate-level JavaScript will get you surprisingly far!</p>
<p>One of the great things about Gatsby is it comes with an entire build setup already configured, which means, if you hate dealing with that stuff (like I do) you can concentrate on the fun stuff, like writing awesome code! Another great thing is the documentation, which is top-notch. After working through the on-site tutorial and building my own site, <a href="https://css-irl.info/">CSS {In Real Life}</a>, my next port of call was to make my site a Progressive Web App.</p>
<h2>Building a Progressive Web App</h2>
<p>A <a href="https://www.gatsbyjs.org/features">comparison table</a> on the Gatsby site shows how its many features stack up compared to other static site generators, and eagle-eyed viewers will spot that it scores very highly in the Performance / PWA section. In fact, I was surprised about how simple the process was.</p><p></p>
<p>One thing to note is your site doesn’t have to be a PWA from the outset – it’s perfectly possible to build a regular site to begin with and convert it to a PWA at a later date.
</p><h3>HTTPS</h3>
<p>PWAs use service workers, extremely powerful web workers, to fetch and cache content, so before your site can become a PWA it must first use HTTPS. My site is hosted with Netlify, which allows very easy integration with Gatsby. Through Netlify it’s a simple step (just a click of a button) to verify your domain, provide a LetsEncrypt certificate and switch a site to HTTPS.</p>
<h3>Installing Gatsby plugins</h3>
<p>Gatsby has a great ecosystem of plugins to automate all sorts of different tasks. We need to install a couple of these in order to provide offline functionality for our PWA:</p>
<ul>
<li><strong><a href="http://www.gatsbyjs.org/packages/gatsby-plugin-manifest/#gatsby-plugin-manifest" target="_blank">Gatsby-plugin-manifest</a></strong> generates the web app manifest, aJSON file containing the instructions for how browsers should display the PWA.</li><li><strong><a href="http://www.gatsbyjs.org/packages/gatsby-plugin-offline" target="_blank">Gatsby-plugin-offline</a></strong> creates the service worker and loads it into the client.</li>
</ul>
<p>Installing Gatsby plugins is a lot like installing node modules - you run <code>npm install [name of plugin]</code>, then add them as an array in your <code>gatsby-config.js</code> file, like in this example.</p>
<pre><code>plugins: [`gatsby-plugin-manifest`, `gatsby-plugin-offline`];
</code></pre>
<p>For our PWA, <code>gatsby-plugin-offline</code> must be listed <em>after</em> <code>gatsby-plugin-manifest</code> in the array.</p>
<p>After installing plugins you need to stop and restart the development server (using command <code>gatsby develop</code>). The manifest JSON file will be generated, then you just need to add your own configuration options – your site title, background colour theme colour and icon – which will improve the visual experience for users and allow them to add your PWA to their home screen (in supporting browsers).</p>
<p>Ordinarily when building a PWA you need to provide multiple icon sizes to account for different displays. With the Gatsby plugin you can just provide one icon at the highest resolution (512px x 512px) and the rest will be generated for you.</p>
<h3>Lighthouse</h3>
<p>After stopping and starting the development server again to allow the changes to take effect, you can then inspect your site in Chrome’s dev tools. We can check our service worker has activated by going to the Application tab. On the left of the Application view you should see the Manifest and Service Worker. Clicking on the service worker tab will show you whether or not the service worker is active.</p>
<p>To see how your site measures up you can use Google’s <a href="http://developers.google.com/web/tools/lighthouse/" target="_blank">Lighthouse</a> tool, which was developed for testing PWAs. This is available from the Audits panel in your dev tools, or as a Chrome extension. You can also run it from the command line by installing a node module. Lighthouse will run an audit of your site, testing for performance, PWA status, accessibility, best practices and SEO, and generate a report with recommendations for improvements. It’s not a necessary part of the process of building a PWA, but I always find it very useful.</p>
<h3>Customising the <head></h3>
<p>One thing you’ll also no doubt want to do to optimise your site (and something that Lighthouse will pick up on) is customise the information in the document <code><head></code> with your site’s metadata. In Gatsby you can’t edit the <code><head></code> directly, so the best way is to use the plugin <a href="https://www.gatsbyjs.org/packages/gatsby-plugin-react-helmet/#gatsby-plugin-react-helmet">gatsby-plugin-react-helmet</a> to give you access to it. Follow the same steps to install the previous plugins. Then you can add the <code><Helmet></code> tag inside your <code><body></code> tag, which will render in the <code><head></code> when compiled:</p>
<pre><code><Helmet
title="CSS {In Real Life}">
<html lang="en-gb" />
<meta name="description" content="CSS In Real Life is a blog covering CSS topics and useful snippets on the web’s most beautiful language. Published by Michelle Barker, front end developer at Mud and CSS superfan."/>
<meta name="keywords" content="css, front end, web development, web design"/>
</Helmet>
</code></pre>
<p>Then you can run <code>gatsby build</code> and publish your site. Congratulations, you now have a Progressive Web App!</p>
Speaking at Bristol JS on Super-powered Layouts2018-07-02T00:00:00Zhttps://css-irl.info/speaking-at-bristol-js/<p>Last Wednesday I had the honour of presenting a new talk at <a href="https://www.meetup.com/BristolJS/">Bristol JS</a> meetup group, alongside veteran speaker, JS whizz and web audio pioneer (also a friend of mine!) <a href="https://ruthjohn.com/">Ruth John</a>. Ruth gave a amazing talk on Web Workers and Worklets – complete with lemmings and <a href="https://codepen.io/Rumyra/full/PaBXdX/">lots of demos</a>, which made me excited to learn more about <a href="https://developers.google.com/web/updates/2016/05/houdini">CSS Houdini</a> and the CSS Paint API.</p>
<p>I’ve only done a handful of talks previously so I was a little nervous about giving my talk, Super-powered Layouts with CSS Grid and CSS Variables (<a href="https://noti.st/mbarker84/G3yvAE/super-powered-layouts-with-css-grid-and-css-variables">slides here</a>), not least about presenting a very CSS-orientated talk at a JS meetup! However, I needn’t have worried. The Bristol JS audience were fantastic – warm, welcoming and asking lots of insightful questions about CSS Grid and CSS variables.</p>
<p>My talk was loosly based on <a href="https://codepen.io/michellebarker/post/super-powered-layouts-with-css-variables-css-gr">this article</a> I wrote a couple of months ago about the power of CSS variables combined with Grid. However, in the talk I gave a lot more time to the basics of Grid, as I was aware plenty of people might not be using it yet.</p>
<p>Interestingly, only around a third of the audience had tried (or were currently using) CSS Grid – a similar proportion to those of us developers using it at <a href="https://ournameismud.co.uk/">Mud</a>. I’d love to get more of an insight into the reasons why so many people aren’t using it yet, despite around 88% browser support worldwide. My impression from talking to people is that two reasons play a big part:</p>
<ol>
<li>Concern about supporting IE11< – still a big issue for some organisations.</li>
<li>It’s such a huge spec that people feel there’s so much to learn, and want to set aside a solid amount of time to sit down and tackle it properly.</li>
</ol>
<p>I get the issues around IE support, and sometimes when you’re working to a deadline there’s a tradeoff between getting something done quickly with the technology you know (such as flexbox or floats) and doing something new that will ultimately benefit your layout (and your learning) but which you need to spend more time providing fallbacks for non-supporting browsers. I do believe that learning Grid is a great investment in skills though, and the more you use it the quicker you become at building Grid layouts, as you become accustomed to familiar patterns. In the Mud pattern library, which we use to quickly build common components, I now keep some regular grid CSS patterns that I frequently find myself reaching for. This not only helps me build layouts quicker (as it includes things like the <code>@supports</code> rules that I need), it can also help the developers who are less familiar with Grid pick it up and start using it in their builds.</p>
<p>This brings me to the second point: many people feel a little daunted by the Grid spec and fear information overload. It’s certainly a large spec because there’s so much you can do with it, but it’s actually not very difficult to start building a very simple grid. As a bare minimum you’ll just need two lines of CSS, something like:</p>
<pre><code>.grid {
display: grid;
grid-template-columns: repeat(6, 200px);
}
</code></pre>
<p>That already gives you a grid of 4 columns, each one 200px wide. Any children of that <code>.grid</code> element will be auto-placed – that is, they’ll each span one column width, and wrap into a new row when each row is filled up.</p>
<p>There is of course so much more you can do with Grid, but don’t let it discourage you. You don’t need to learn it all at once. Each new property you discover gives you a little more power to your layouts. It can still be used in conjunction with other properties, like flexbox – if fact, Grid and flexbox work really well together. You don’t have to choose between one thing or another.</p>
<h2>Is Grid the silver bullet?</h2>
<p>One of the questions I was asked at the end of my talk is whether Grid is the silver bullet for layout that we’ve all been looking for. My answer is yes and no. Grid can’t do <em>everything</em> yet. Subgrid, which will allow the grid parameters to be passed down to the grid children, is under consideration for <a href="https://www.w3.org/TR/css-grid-2/">Level 2</a>, with every indication that it will be implemented in some way. But there’s still no sign of a true masonry layout implementation in CSS, which would be extremely useful. However, CSS is gaining new super powers constantly, and I’m confident that in time we’ll see all our layout conundrums solved with CSS. Not only that, but CSS Grid, couple with clip-path(), <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Shapes">Shapes</a> and more, is sure to usher in a new era of creativity on the web, allowing us to build previously unimaginable layouts.</p>
<p>In the words of Ruth (on that very day):</p>
<blockquote>
<p>Everybody get on the CSS train!</p>
</blockquote>
Progressive Web Apps: An Introduction2018-06-29T00:00:00Zhttps://css-irl.info/progressive-web-apps/<p>If you’re in the business of building websites, you may have increasingly heard people talking about <a href="http://developers.google.com/web/progressive-web-apps/">Progressive Web Apps</a> (PWAs). Developed by Google, PWAs have all the advantages of the web when it comes to user engagement, coupled with the speed and reliability users can expect from a native app.</p>
<h2>Why Progressive Web Apps?</h2>
<p>The key selling points are:</p>
<h3>Performance</h3>
<p>A PWA loads fast and responds quickly to user interaction without any janky animation.</p>
<h3>Reliability</h3>
<p>PWAs are always able to deliver an experience to the user, even offline or in uncertain network conditions.</p>
<h3>Better user experience</h3>
<p>PWAs should feels natural on the device, like a native app. They can even serve push notifications, add an icon to a user’s home screen and remove the browser chrome, all providing a more native-like experience.</p>
<p>Google is heavily invested in this technology, providing a whole host of resources, including free developer training, Lighthouse (a browser-based tool for testing your PWAs) and extensive documentation, including a PWA checklist.</p>
<h2>Offline first</h2>
<p>Applications increasingly need to perform in shaky network conditions. When a browser is offline, a service worker allows you to serve a custom offline page, or assets from the cache, to ensure users still get a basic (if limited) experience. The Washington Post, for example, caches the top stories, enable the user to access these when offline.</p>
<h2>Do you need a PWA?</h2>
<p>You might want to sit down and consider whether you absolutely need your site to be a PWA. Ask yourself these questions:</p>
<ul>
<li>
<p>Does the content update regularly?</p>
</li>
<li>
<p>Are users likely to want to browse offline?</p>
</li>
<li>
<p>Do they need real-time updates and push notifications?</p>
</li>
<li>
<p>Are they likely to be using a browser that <a href="https://jakearchibald.github.io/isserviceworkerready/">supports service workers</a>? (We’ll come back to this shortly.)</p>
</li>
<li>
<p>If the answer is “yes” to all (or most of these), then a PWA may be worthwhile. (As a side note regarding point four, users of non-supporting browsers won’t have their experience hindered by the presence of a service worker (the browser will just ignore it) – but it’s worth taking into account before you spend considerable time and energy on a PWA.)</p>
</li>
</ul>
<p>It’s worth mentioning though, that a PWA is not a quick fix for your performance problems. You’ll probably want to make sure you’ve already taken all other steps to optimise your site – that is to say, mobile-first responsive design, HTML5 standards, responsive and lazyloaded images, compressing and minifying assets, and inlining critical CSS. Once all criteria are satisfied, making your site a PWA has plenty of benefits.</p>
<h2>How do you build a PWA?</h2>
<p>So how do you go about building a PWA? Well, that’s not quite so straightforward. I’m not going to go into technical details in this article, but I’ll list some resources at the end, so skip to there if you’re keen to get building!</p>
<p>PWAs rely on Javascript promises and the <a href="https://developers.google.com/web/updates/2015/03/introduction-to-fetch">Fetch API</a>, so you’ll probably want to get to grips with those. The other key technology for a PWA is service workers.</p>
<h3>Service workers</h3>
<p>A service worker is a type of web worker that is installed client-side when your application loads, and runs in the background, separate from the main browser thread. The primary uses of service workers are to handle network requests and store content for offline use, and to handle push notifications. They are incredibly powerful, and therefore can only be used with the HTTPS protocol.</p>
<p>Smart use of caching with a service worker can make your site load super fast on when users make repeated visits.</p>
<h3>Web App Manifest</h3>
<p>PWAs can provide an experience that feels (to the user) like using a native app, including adding an icon to the home screen (rather than the user having to navigate to the site through their browser). The web app manifest is a JSON file that tells the browser how to display your PWA. It’s where you can define the icon for users to add your app to their home screen. It’s currently supported in most mobile browsers (iOS, Chrome on Android), as well as MS Edge, but not yet supported in other desktop browsers.</p>
<h2>Resources</h2>
<p>If you’re ready to get started building a PWA, here are some helpful resources:</p>
<ul>
<li>
<p><strong><a href="https://developers.google.com/web/progressive-web-apps/">Your First Progressive Web App</a></strong><br />Google’s step-by-step guide to building a PWA</p>
</li>
<li>
<p><strong><a href="https://addyosmani.com/blog/getting-started-with-progressive-web-apps/">Getting Started with Progressive Web</a></strong> Apps by Addy Osmani</p>
</li>
<li>
<p><strong><a href="https://developers.google.com/web/progressive-web-apps/checklist">Progressive Web App Checklist</a></strong></p>
</li>
<li>
<p><strong><a href="https://abookapart.com/products/going-offline">Going Offline</a></strong> by Jeremy Keith (book)<br />Widely recommended by leading figures in the industry as a comprehensive guide to taking your site offline.</p>
</li>
</ul>
<p><strong>Coming soon:</strong> I’ll share my experience of building a PWA with static site generator <a href="https://www.gatsbyjs.org/">Gatsby</a>!</p>
In Defence of Bad Code2018-06-14T00:00:00Zhttps://css-irl.info/in-defence-of-bad-code/<p>I saw a tweet the other day from <a href="https://twitter.com/codepo8">Christian Heilmann</a> of Microsoft (formerly of Mozilla), a big name in the web industry and tech conference circuit:</p>
<blockquote class="twitter-tweet" data-lang="en"><p lang="en" dir="ltr"><button class="button button--primary button--dark button--chromeless button--withIcon button--withSvgIcon u-padding12 u-borderRadius4 u-borderDarker" data-action="play-audioPlayer"><br /><br />Somewhere along the path, we replaced CSS values with classes.</p>— Chris Heilmann (@codepo8) <a href="https://twitter.com/codepo8/status/1006765051116695552?ref_src=twsrc%5Etfw">June 13, 2018</a></blockquote>
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>It wasn’t the tweet itself that rankled – I myself have rolled my eyes at similar chunks of code on occasion, and Christian didn’t explicitly make any further comment on it. It was the replies in the thread below that got to me. Because they were nearly all (at the time of writing), along the lines of “Oh my god, that’s disgusting, how could someone write code like that”, and of course, the sanctimonious “I myself never saw the point in [utility classes, BEM, etc - insert here]”. People seemed to take vindication from the original tweet, as if it proved that they had been right all along, and that it was always inevitable that these new-fangled CSS methodologies were always going to lead straight to hell - and of course, they would never write code like that themselves.</p>
<p>Leaving aside for a moment the fact that we’re talking about A BUTTON – and users don’t give a crap how many classes a thing has, as long as it looks like a button and behaves like a button – it’s all too easy for me to imagine how this poor little element’s code could have got to this point. And I’m pretty sure I’m not the only one who can recall writing code like this at some time or another.</p>
<p>Picture the scene, if you will:</p>
<p><strong>Dev 1:</strong> Hi, welcome to the team! We use BEM here. Anyway, here’s a UI to build.</p>
<p><strong>Dev 2:</strong> Ok, cool. Well, I can see that this thing here is going to be a button, so I’m going to call it <em>button</em>. That way whenever someone uses it they’ll have a thing that looks like a button. Even when we want to style an anchor tag to look like a button (which happens a lot), we’ll be able to use this class.</p>
<p><em>Later…</em></p>
<p><strong>Dev 2:</strong> This button needs an icon, which requires me to style the button a little differently, but I want to keep my code as DRY and reusable as possible, so I’m going to create a modifier class called <em>button--withIcon</em>.</p>
<p><em>Still later…</em></p>
<p><strong>Dev 2:</strong> We’ve been using icon fonts everywhere, but it turns out this button’s icon needs to animate a little bit, so I’m going to use an SVG for that instead. I’ll add this class, <em>button--withSVGIcon</em> so I can style the icon’s animations in my <em>_button.scss</em> file, since they’re specific to buttons only. I want to keep all the styles from <em>button--icon</em> and just layer these animation styles on top, so I’ll leave both classes in there.</p>
<p><em>Still later...</em></p>
<p><strong>Designer:</strong> We’ve got some feedback from the client, and this particular button’s going to be used on a light background, so we need to make the colour a bit darker here so it’s more user-friendly. We don’t want to affect any of the other places the button is going to be used on the site.</p>
<p><strong>Dev 2:</strong> Ok, I’ll add a modifier class: <em>button--dark</em> will give a darker variant of the button, for light backgrounds.</p>
<p><strong>Designer:</strong> We also want to get rid of that chrome effect here, it’s not right for this particular button.</p>
<p><strong>Dev 2:</strong> Great, <em>button--chromeless</em> will be useful for when we want flat-style buttons.</p>
<p><em>6 months later…</em></p>
<p><strong>Dev 3:</strong> Right I need to do some quick amends to this video component with the button here. I don’t want to mess with those existing classes as they’re probably being used elsewhere, but we’ve been taking a utility-first approach to CSS lately, so I’m just gonna add a couple of utility classes to add a few little styling tweaks to this one.</p>
<p>It’s too hard to imagine how that class list could get out of hand.</p>
<p>Any what’s so bad about this code, anyway? Do those classes solve the problems at hand? Probably. Do they hinder the user? Unlikely. The developer who has to maintain it is maybe inconvenienced, but on the other hand, those classes tell us more or less what that piece of the UI should look like and where we can find the corresponding CSS, and they’re reusable too, so maybe it’s not all bad.</p>
<p>How often do you go back to old projects and look at your code – any code – and think “Ugh, what was I thinking?”. For me anyway, it’s pretty often. Sure, you start every project with the best intentions of keeping your code super clean and tidy, and for a while maybe all goes to plan. But in an agency environment, before long you’re rushing to meet deadlines and get projects out the door. You have to weigh up time spent refactoring against adding a quick and dirty hack that solves the problem at hand. And quite often, that’s good enough. Don’t feel bad about it, move on and try to learn lessons for next time.</p>
<p>In a large, constantly evolving codebase that needs to be maintained then refactoring is probably worthwhile, but sometimes it’s not.</p>
<p>With the best will in the world, projects evolve, functionality needs to be bolted on, and often a quick fix with a utility class is preferable to 2+ hours of refactoring, especially when you know that a project doesn’t need to be maintained all that often. Since I first read the Twitter thread many people have added replies suggesting many different methodolgies for improving this button’s code. Which kind of proves my point.</p>
<p>So let’s not code-shame each other. Each project and each team has its own needs. Maybe you like BEM, or maybe you prefer SMACSS. Maybe you favour utility classes, perhaps as few classes as possible. Maybe CSS-in-JS is more your bag. All are perfectly valid, each has different strengths and weaknesses. As long as you try to stick with one and use it consistently (especially in a team) things will be fine, and if you have to throw in some hacky stuff every now and then the world probably won’t end. We have better tools than ever before to help us debug even the messiest code, and while you should always try to leave a codebase in a better state than you found it, even that is subjective.</p>
<p>And, if in doubt, always remember the golden rule: Comment The F*** Out Of It.</p>
<p>Thanks for reading this far. As a side-note, <a href="https://twitter.com/rachsmithtweets/status/1007023291389784064">this Twitter thread</a> from <a href="https://twitter.com/rachsmithtweets">Rachel Smith</a> says all this way more concisely.</p>
Great Front End Writers to Follow2018-06-12T00:00:00Zhttps://css-irl.info/top-front-end-writers/<p>As this is a brand new blog, I want to highlight some of the people in web development who have inspired me and whose articles (and writing style) I love.</p>
<p>When I was thinking about starting <strong>CSS {In Real Life}</strong> I just wanted a place I could put all my articles, whether long or short, and any random thoughts about CSS and front end development in general. My aim is to write about what I discover, and hopefully demystify a few things for whoever takes the time to read the articles. It might not always be perfect, but I find writing a great way to learn, and to pass on knowledge.</p>
<p>The people I’ve taken inspiration from are hugely knowledgeable about their field, and their passion for the web shines through in their articles. I aspire to write articles that are as useful to others as theirs have been to me. I encourage you to read their writing, and to write about and share your own discoveries and explorations, if you feel able.</p>
<p>Here they are, in no particular order…</p>
<h2><a href="https://www.sarasoueidan.com/">Sara Soueidan</a></h2>
<p>Sara’s ability to take a topic and drill down into it, examining it from every angle is unsurpassed. I’ve learnt so much about CSS layout and SVG from reading her posts. She has a gift for explaining things in a way that anyone can understand.</p>
<h3>Recommended reading:</h3>
<p><a href="https://www.sarasoueidan.com/blog/optimizing-svg-delivery-with-svg/">Optimizing SVG Text & Image Delivery with Inline SVG</a></p>
<h2><a href="https://zellwk.com/">Zell Liew</a></h2>
<p>When I was getting into CSS layout methods in a big way, Zell’s blog was where I got a huge amount of information from, particularly on Susy, the Sass grid framework (before CSS Grid was widely supported). Now he covers a wide range of topics - he’s recently published a JavaScript course, so his blog features lots of JS articles (and now video). His style of writing is very personable and beginner-friendly - he never assumes any prior knowledge and takes care to explain thoroughly.</p>
<p>He isn’t afraid to share more personal posts about his journey as a developer, which is great to read whenever you feel less than 100% confident in your work – as most of us do at one time or another!</p>
<h3>Recommended reading:</h3>
<p><a href="https://www.smashingmagazine.com/2018/05/future-of-web-design/">New CSS Features That Are Changing Web Design</a></p>
<h2><a href="https://rachelandrew.co.uk/">Rachel Andrew</a></h2>
<p>Rachel is a font of knowledge on all things CSS layout-related, which means I frequently find myself reading her articles, whether on her own blog or on Smashing Magazine. She has a very clear and direct way of writing, extremely helpful for a topic as vast as Grid layout.</p>
<h3>Recommended reading:</h3>
<p><a href="https://rachelandrew.co.uk/archives/2017/08/08/changes-to-the-grid-spec-and-taking-on-multi-column-layout/">Changes to the Grid Spec and taking on Multi-Column layout</a></p>
<h2><a href="https://thenewcode.com/">Dudley Storey</a></h2>
<p>I’ve used many of Dudley’s tutorials over the past few years - there was a time when literally every CSS problem I had, Dudley had the solution in his blog! His articles are useful step-by-step walk-throughs, with creative, visual examples, and are a great for introducing new concepts.</p>
<h3>Recommended reading:</h3>
<p><a href="http://thenewcode.com/1124/Rotating-Elements-To-Mouse-and-Touch-Locations-Using-JavaScript">Rotating Elements To Mouse and Touch Locations Using JavaScript</a></p>
<h2><a href="https://una.im/">Una Kravets</a></h2>
<p>I love Una’s great mix of informative articles and visual, interactive demos. Her blog feels like a goldmine, with lots of useful nuggets to uncover, heavily focused on CSS. I like how she includes her own blog articles alongside others with external links posted on other sites like CSS Tricks.</p>
<h3>Recommended reading:</h3>
<p><a href="https://css-tricks.com/solved-with-css-dropdown-menus/">Solved with CSS: Dropdown Menus</a></p>
<h2><a href="https://bitsofco.de/">Ire Aderinokun</a></h2>
<p>Bitsofco.de is another one that constantly crops up when I’m Googling for a particular CSS property or HTML attribute. Written by Ire Aderinokun, the site is chock-full of super useful information, well written and thoroughly explained, with lots of code examples and real-world use cases.</p>
<h3>Recommended reading:</h3>
<p><a href="https://bitsofco.de/how-display-contents-works/">How display:contents works</a></p>
Practical Tips for Working with CSS Variables2018-06-01T00:00:00Zhttps://css-irl.info/practical-tips-css-variables/<p>I’ve been playing around with <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_variables">CSS variables</a> (or custom properties) quite a lot recently and thought I’d share a few tips as I develop a practical strategy for integrating them into my workflow.</p>
<h2>Types of variables</h2>
<p>When working with CSS Variables often find it helps to think of them as two different types:</p>
<h3>Primary variables</h3>
<p>These are the variable that you might want to update in your CSS - in different selectors, within media queries or when targeting with <code>:hover</code> or <code>:focus</code>, for example, or change with JavaScript. They generally contain a single value:</p>
<pre><code>:root {
--wrapper: 900px;
--gutter: 10px;
}
</code></pre>
<h3>Secondary variables</h3>
<p>These are variables that are calculated from other variables. For example, in this demo on aspect ratio grid cells the <code>--rowHeight</code> variable is calculated from several primary variables. It is applied to a property but never updated manually - only recalculated as a result of updating the primary variables.</p>
<p>It can be useful to prefix secondary variables, like in this example, so that you and other people working with your code know that they shouldn’t be changed manually:</p>
<pre><code>:root {
--wrapper: 900px;
--gutter: 10px;
/*
the s- prefix denotes a secondary variable
*/
--s-rh: calc((var(--wrapper) - (3 * var(--gutter))) / 4);
}
</code></pre>
<p>The only exception would of course be if you need to change how the value is calculated - but theoretically once set it should be permanent. This could help discourage developers who are unfamiliar with the code from tinkering with it unless necessary.</p>
<h2>Scoping</h2>
<p>In many of my demos I’m declaring the variables <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:root">in <code>:root</code></a>, which represents the <code><html></code> element:</p>
<pre><code>:root {
---bgColor: red;
}
</code></pre>
<p>However, this isn’t strictly necessary and, in fact, isn’t really good practice either. Many of the reasons for avoiding setting global variables in Javascript also apply to CSS. If you then wanted to use a variable for <code>background-color</code> called <code>--bgColor</code> in different components you could run into all sorts of problems with scoping. It’s a better idea to declare the variables in a selector, e.g. if you’re working in components:</p>
<pre><code>.my-component {
---bgColor: red;
}
.some-other-component {
---bgColor: blue;
}
</code></pre>
<p>In the snippet above, <code>--bgColor</code> is scoped to each component, so you can use a variable with the same name without fear of it affecting anything outside of that component.</p>
<h2>Setting defaults</h2>
<p>With CSS Variables you can set a <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_variables">fallback value</a> (or multiple values). This means in some situations you only need to declare your variables at the point they need to change. In this demo the variable <code>--bgColor</code> for the box is only declared after the breakpoint – up until that point it takes on the default value:</p>
<iframe height="331" scrolling="no" title="CSS Variable default values" src="https://codepen.io/michellebarker/embed/PaqMpy/?height=331&theme-id=0&default-tab=css,result&embed-version=2" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;">See the Pen <a href="https://codepen.io/michellebarker/pen/PaqMpy/">CSS Variable default values</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>In this second example you can see that the <code>h1</code> and <code>p</code> selectors have different default values for their <code>color</code> property, but both take on the variable when hovered:</p>
<iframe height="333" scrolling="no" title="CSS Variable with defaults" src="https://codepen.io/michellebarker/embed/PaPmwr/?height=333&theme-id=0&default-tab=css,result&embed-version=2" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;">See the Pen <a href="https://codepen.io/michellebarker/pen/PaPmwr/">CSS Variable with defaults</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<h2>Using CSS Variables with preprocessor variables</h2>
<p>One of the drawbacks of CSS Variables is they don’t work in media queries or selector names – e.g. <code>:nth-child(var(--n))</code> would not work. So it’s likely you’re still going to want to use preprocessor variables to some degree.</p>
<p>I would caution against mixing the two unless you fully understand their different characteristics. Sass variables are compiled before your code hits the browser, whereas CSS variables don’t take on their computed value until they hit the browser. That means that in the example below the width value for <code>.box1</code> (using Sass variables) will work, but <code>.box2</code> will throw an error because the value for <code>--halfWidth</code> is passed to the browser as a string:</p>
<pre><code>$width: 600px;
$halfWidth: $width / 2;
:root {
--halfWidth: $width / 2;
}
.box1 {
width: $halfWidth;
}
.box2 {
width: var(--halfWidth); // this isn’t valid
}
</code></pre>
<p>You can, however, use <code>calc()</code>, as in previous examples. See the result in the demo here:</p>
<iframe height="328" scrolling="no" title="Preprocessor variable with CSS Variables" src="https://codepen.io/michellebarker/embed/aKvWyK/?height=328&theme-id=0&default-tab=css,result&embed-version=2" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;">See the Pen <a href="https://codepen.io/michellebarker/pen/aKvWyK/">Preprocessor variable with CSS Variables</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>If you inspect the element in Chrome and go to the Computed Styles tab you can see that the width value for <code>.box2</code> is not computed. At <a href="http://ournameismud.co.uk/">Mud</a> we use a lot of Sass functions, for example to calculate rems from pixels for sizing. I found this was an issue when I attempted to pass a Sass function into a CSS variable, e.g. <code>--width: rem(600px)</code>. There are PostCSS plugins that can convert pixels to rems and achieve the desired result, but I’d need to experiment a bit more with these before I feel confident recommending them for use with CSS Variables.</p>
<p>Nevertheless, there are some scenarios where using preprocessor variables in the same block of code as CSS Variables makes sense, such as in media queries, as mentioned previously.</p>
<p>In this demo I’m using Sass variables for the fallback values in the CSS variable, as well as the media query to provide a handy visual breakpoint helper:</p>
<iframe height="332" scrolling="no" title="Visualising breakpoints with CSS Variables" src="https://codepen.io/michellebarker/embed/pKJMJM/?height=332&theme-id=0&default-tab=css,result&embed-version=2" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;">See the Pen <a href="https://codepen.io/michellebarker/pen/pKJMJM/">Visualising breakpoints with CSS Variables</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
Introduction to Gatsby2018-05-29T00:00:00Zhttps://css-irl.info/introduction-to-gatsby/<p>In case you haven’t heard about it, <a href="https://www.gatsbyjs.org/">Gatsby</a> is the latest hot thing in static site generators. While many static site generators (SSGs) use templating languages like Handlebars or Mustache, Gatsby uses React, helping contribute to its “blazing fast” claim.</p>
<p>Having never used an SSG before (although I’ve read about them), and with very limited experience with React, what better way to dip my toe into the water than using Gatsby to publish my brand new blog (this very one, in fact!)?</p>
<h3>Getting started</h3>
<p>It turns out that getting started with Gatsby is pretty easy. The Gatsby site features a fantastic, step-by-step tutorial that walks you through getting your first site up and running (even down to deploying), and contains a wealth of resources for when you’ve mastered the basics. You don’t need any prior experience with React, although working with Gatsby definitely made me want to learn React to take full advantage of its power.</p>
<p>One thing that prohibits me from publishing side projects is the thought of configuring a whole build setup with Gulp, webpack, etc. just for something small. This is where Gatsby really appeals to me. Gatsby provides an entire out-of-the-box build setup. Yep. Run <code>gatsby develop</code> and immediately you can bypass all that configuration and start building cool stuff in the browser (with live reloads). Run <code>gatsby build</code> and your production code is optimised (including progressive image loading and code splitting, among <a href="https://www.gatsbyjs.org/features/">many other features</a>) with <em>absolutely no effort on your part whatsoever</em>. You could feasibly get a small, performant site built and deployed in just minutes. You can also add a custom webpack config if you need to.</p>
<h3>CSS</h3>
<p>There are two options when it comes to writing CSS for your components: CSS-in-JS (using libraries such as Glamor and Styled Components) or CSS Modules. As I feel far more comfortable writing “traditional” CSS, I opted for the latter. <a href="https://github.com/css-modules/css-modules">CSS Modules</a> are really interesting, and not tied to Gatsby. Styles are scoped locally by default and unique class names are generated programmatically, meaning you don’t get the clashes with naming and specificity that you sometimes get in traditional CSS, and you can compose new selectors from others. I feel like I’ve only scratched the surface of CSS Modules, and am looking forward to experimenting further.</p>
<p><em>(Side note: This doesn’t mean I have a problem with traditional CSS, or that I’m advocating CSS Modules in every scenario! Many of the so-called “problems” of CSS are actually down to misuse or misapplication. Nevertheless, CSS Modules and CSS-in-JS are good to know about, and can be useful tools to have in your arsenal.)</em></p>
<h3>Plugins</h3>
<p>There is a whole ecosystem of <a href="https://www.gatsbyjs.org/docs/plugins/">Gatsby plugins</a> that you can pick and choose to tailor your project. I’m using <code>gatsby-plugin-sass</code> to enable me to write Sass instead of regular CSS, <code>gatsby-transformer-remark</code> for markdown files and <code>gatsby-plugin-typography</code> which sets you up with some nice typography combos with minimal configuration. Authoring your own plugins and contributing to the Gatsby community is encouraged, if that’s your bag.</p>
<h3>Data</h3>
<p>You could of course hard code all your content, but it’s likely you’ll want some sort of dynamic content on your site.</p>
<p>Gatsby’s data layer is powered by GraphQL, and the tutorial walks through how to build database queries with Gatsby’s GraphiQL tool. It’s very visual, showing you exactly what your database queries will return, which for someone like me (who finds anything database-related a little scary) is a blessing.</p>
<p>I write my blog posts in markdown files, but you could configure Gatsby to work with Github Pages, or the CMS of your choice for a better editorial experience.</p>
<h3>Deployment</h3>
<p>After you’ve run the build command, Gatsby allows near-instantaneous deployment through <a href="https://surge.sh/">Surge</a>, <a href="https://www.netlify.com/">Netlify</a> and others. I set up my hosting with Netlify – run <code>gatsby deploy</code>, set up a Netlify account and you’re good to go. You can then configure Netlify to auto-deploy whenever you push to a repository. Netlify provides a simpler and more helpful experience than other hosting providers I’ve used, with helpful documentation, taking all the stress away from launching a site!</p>
<h3>In conclusion</h3>
<p>I thoroughly recommend giving Gatsby a go if you’re on the fence about trying a static site generator and want to get a simple site up and running quickly and easily. The amount of thought that’s gone into every aspect of the onboarding process and the developer experience is absolutely fantastic. And, have just received a shed load of seed money to <a href="https://www.gatsbyjs.org/blog/2018-05-24-launching-new-gatsby-company/">launch as a startup</a>, it’s only going to get better.</p>
Aspect Ratio Cells with CSS Grid Layout2018-05-27T00:00:00Zhttps://css-irl.info/aspect-ratio-cells/<p>I found myself recently building a layout in CSS Grid that would have previously needed JavaScript in order to work. It’s a layout based on equal sized square grid cells, where grid items could span one or two cells on the row and/or column axis. In other words, the grid <em>cells</em> needed to maintain an aspect ratio (1:1 in this case), but the actual grid <em>items</em> did not necessarily, something like this:</p>
<p><img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/85648/Grid%201.svg" alt="1:1 aspect ratio grid" /></p>
<p>Chris Coyier explores aspect ratio with Grid in <a href="https://css-tricks.com/aspect-ratios-grid-items/">a post here on CSS Tricks</a>, and documented a solution very similar to the layout I was trying to build, using the relatively well-known <a href="https://css-tricks.com/aspect-ratio-boxes/">padding hack</a>. The downside is that the aspect ratio acts on the grid children, not on the parent grid container. If you don’t place items in grid track row (if you wanted an empty row, for example) the grid cells would collapse. In this illustration, the cells outlined in red would collapse to a height of zero (or whatever height we set on the <code>grid-template-rows</code> property):</p>
<p><img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/85648/Grid%202.svg" alt="1:1 aspect ratio grid, with cells that would collapse highlighted in red" /></p>
<p>My goal was to be able to define the aspect ratio on the grid cells themselves, so that they would always maintain this ratio even when empty. I wanted to see if, using <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_variables">CSS Variables</a> (also known as custom properties), I could figure out a solution that might be work for me, allowing me to create grid cells of any aspect ratio I choose.</p>
<p>The solution I came up with might not work for everyone in all situations, as it’s dependent on knowing the width of either the outer container, and (as it’s based on using <code>calc()</code> to calculate the grid row height) this must be either a fixed value or viewport unit, it can’t be a percentage. I wouldn’t recommend creating aspect ratio boxes this way in general, as often with responsive design we don’t know the exact width of any given item. However, when working with Grid I generally find that I <em>do</em> know the width of the outer grid wrapper. (More on this later.)</p>
<p>First we need a few values:</p>
<ul>
<li>Wrapper width</li>
<li>Grid gutter width</li>
<li>Number of columns</li>
<li>The aspect ratio we want to achieve for our grid cells</li>
</ul>
<p>I’m going to save these values as variables, which will be useful when it comes to doing the calculations for the grid:</p>
<pre><code>:root {
// We just need to know these 3 values up front:
--wrapper: 100vw; // e.g. Must be a viewport unit or fixed value, e.g. 100vw, or 1200px
--gutter: 10px;
--noOfColumns: 4;
// And our aspect ratio:
--ratioA: 1;
--ratioB: 1;
}
</code></pre>
<p>We’re going to use <code>grid-template-columns</code> and <code>grid-auto-rows</code> to define our grid.</p>
<p>*Side note: <code>grid-auto-rows</code> is a useful alternative to <code>grid-template-rows</code> for defining <a href="https://css-tricks.com/difference-explicit-implicit-grids/">implicitly created grid tracks</a>. It’s possible we don’t know how many rows there will be, but we want them all to be the same height.</p>
<p>This will look something like the following:</p>
<pre><code>.grid {
width: var(--wrapperWidth);
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-auto-rows: var(--rowHeight);
grid-gap: var(--gutter);
}
</code></pre>
<p>The <code>--wrapperWidth</code> and <code>--gutter</code> values should be our known values. The <code>--rowHeight</code> value is the one we want to calculate. We can use our <code>--wrapperWidth</code> and <code>--gutter</code> variables to calculate the row height. For a grid with an aspect ratio of 1:1, the calculation is relatively simple:</p>
<pre><code>:root {
--wrapperWidth: 100vw;
--gutter: 10px;
--noOfColumns: 4;
--rowHeight: calc((var(--wrapperWidth) - (3 * var(--gutter))) / 4); // where 4 is the number of columns and 3 is the number of gutters
}
</code></pre>
<p>Then we can use the <code>--rowHeight</code> value in in our grid-auto-rows property:</p>
<pre><code>.grid {
width: var(--wrapperWidth);
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-auto-rows: var(--rowHeight);
grid-gap: var(--gutter);
}
</code></pre>
<p>This will result in grid cells with a height the same as their width:</p>
<iframe height="338" scrolling="no" title="Aspect ratio Grid boxes with CSS Variables" src="https://codepen.io/michellebarker/embed/mLprKY/?height=338&theme-id=0&default-tab=html,result&embed-version=2" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;">See the Pen <a href="https://codepen.io/michellebarker/pen/mLprKY/">Aspect ratio Grid boxes with CSS Variables</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<h3>Placing items</h3>
<p>In this particular grid I can let CSS Grid’s auto placement algorithm do most of the work. Most of the grid items should span a single cell and follow the normal flow. The only items I want to place explicitly are the large grid items, which span two tracks in either direction:</p>
<pre><code>.grid__item--lg {
grid-column: span 2;
grid-row: span 2;
}
.grid__item--right {
/*
the second large item needs to align to the right,
so I’m specifying that it should end at the last grid line
*/
grid-column-end: 5;
}
</code></pre>
<p>If I want the rest of the items to fill in any spaces I can use <code>grid-auto-flow: dense;</code> on the grid container, otherwise any subsequent items will simply take the next place in the flow.</p>
<h3>Variables for columns</h3>
<p>For this particular grid I know that I always want it to have four columns (and therefore 3 gutters between columns), so to keep the calculation simple I’ve hard coded these numbers in the <code>calc()</code> function. But if we want the grid adapt to different situations (e.g. we want to increase the number of columns visible on large screens) then we could use the <code>--noOfColumns</code> variable instead:</p>
<pre><code>:root {
--wrapper: 100vw;
--gutter: 10px;
--noOfColumns: 4;
// Number of gutters is columns minus 1:
--noOfGutters: calc(var(--noOfColumns) - 1);
// Calculating the row height:
--rowHeight: calc((var(--wrapperWidth) - (var(--noOfGutters) * var(--gutter))) / var(--noOfColumns));
@media (min-width: 42em) {
--noOfColumns: 6 // increasing the number of columns after the 42em breakpoint
}
}
.grid {
/*
Don’t forget to use the variable here too:
*/
...
grid-template-columns: repeat(var(--noOfColumns), 1fr);
...
}
</code></pre>
<h3>Dealing with Overflow</h3>
<p>We might also want the cells to expand if the content overflows the grid cell. With Grid we can use <code>minmax()</code> to allow the grid cells to expand vertically to fit the content:</p>
<pre><code>.grid {
...
grid-auto-rows: minmax(var(--rowHeight), auto); /* This tells the grid that
the cells must be a minimum of our row height value, but should expand if
the content is longer */
...
}
</code></pre>
<p>If we want to force cells to maintain aspect ratio, even when there is overflow, we can just omit <code>minmax()</code> and use the calculated value instead.</p>
<h3>Changing the aspect ratio</h3>
<p>What about if we want our grid cells to be a different aspect ratio, e.g. 16:9 or 4:3? This is where defining our number of columns, and our aspect ratio values comes in. It makes our row height calculation a bit more complex as we need to calculate variables from other variables, but you only need to write out that calculation once – if you update the values of any of the primary variables then the grid row height will be calculated automatically:</p>
<iframe height="341" scrolling="no" title="Aspect ratio Grid boxes with CSS Variables - with variable ratios" src="https://codepen.io/michellebarker/embed/wjjqex/?height=341&theme-id=0&default-tab=css,result&embed-version=2" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;">See the Pen <a href="https://codepen.io/michellebarker/pen/wjjqex/">Aspect ratio Grid boxes with CSS Variables - with variable ratios</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>If we wanted we could now have multiple grids on a page with different aspect ratios, only updating the two variables for each one.</p>
<h3>CSS Variables vs. preprocessor variables</h3>
<p>We can acheive a very similar outcome using preprocessors like Sass instead of CSS Variables. There are advantages and disadvantages to either method. With Sass we can save the calculation as a function and call it when defining our grid. We could even have different aspect ratios for different grid rows, like in this example:</p>
<p data-height="339" data-theme-id="0" data-slug-hash="VxdXRY" data-default-tab="css,result" data-user="michellebarker" data-embed-version="2" data-pen-title="Aspect ratio Grid boxes with Sass (with function)" class="codepen">See the Pen <a href="https://codepen.io/michellebarker/pen/VxdXRY/">Aspect ratio Grid boxes with Sass (with function)</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.</p>
<script async="" src="https://static.codepen.io/assets/embed/ei.js"></script>
<p>This example actually compiles to <em>fewer</em> lines of CSS than the equivalent example with CSS Variables.</p>
<p>On the other hand, with CSS Variables, if we wanted to create another grid with a different aspect ratio (or change our ratio at different breakpoints, all we need to do is update the variables, rather than writing out the whole <code>grid-auto-rows</code> property again:</p>
<pre><code>.grid--1-1 {
--ratioA: 1
--ratioB: 1
}
.grid--16-9 {
--ratioA: 16
--ratioB: 9
}
</code></pre>
<p>I would caution against mixing preprocessor variables and CSS Variables, and in my opinion CSS Variables lend themselves very well to helping build flexible and maintainable layouts with CSS Grid, as <a href="https://codepen.io/michellebarker/post/super-powered-layouts-with-css-variables-css-gr">I’ve written previously</a>. Whichever method you choose is a matter of personal preference, and what works for you and your team!</p>
<h3>Making it responsive</h3>
<p>As I wrote earlier, this method relies on knowing the outer width of our grid container in order to calculate the row height. Quite often you would probably want the grid to span the full viewport width up until a given breakpoint, possibly with a standard margin or padding either side. With CSS Variables we can adjust any of the values at different breakpoints, including the wrapper width. Here’s a working example, where I’m altering the wrapper width, number of columns and aspect ratio at larger breakpoints:</p>
<iframe height="337" scrolling="no" title="Aspect ratio Grid boxes with CSS Variables - responsive" src="https://codepen.io/michellebarker/embed/dexNXK/?height=337&theme-id=0&default-tab=html,result&embed-version=2" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;">See the Pen <a href="https://codepen.io/michellebarker/pen/dexNXK/">Aspect ratio Grid boxes with CSS Variables - responsive</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>CSS Grid syntax with CSS Variables can look intimidating, but if you give it a chance and start playing around with the two of them, you might find it a helpful tool to build complex layouts!</p>
CSS Grid: More Flexibility with Minmax()2018-04-29T00:00:00Zhttps://css-irl.info/more-flexibility-with-minmax/<p>One of the things that can put people off trying out CSS Grid is there’s so much choice when it comes to defining your grid layout and placing your content, so it can be hard to know until you’ve been using it a while which is going to to be the best option for a particular layout. It’s only recently that I got around to fully appreciating the power of <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/minmax"><code>minmax()</code></a> and how it can be such a huge helper when coding many of the layouts I’m required to build, so I’d like to share one way in which it’s been beneficial to me.</p>
<p><code>minmax()</code> is a function that can be used in your <code>grid-template-columns</code> or <code>grid-template-rows</code> property to size your grid tracks. It takes (you guessed it) a minimum value and a maximum value, which can be a length (pixels, ems, etc.), a percentage, a flexible <code>fr</code> unit or a keyword. <a href="http://bitsofco.de/how-the-minmax-function-works/">Here’s a good article explaining it in depth</a>. Jen Simmons also discusses <code>minmax()</code> and the future of layout in <a href="https://www.youtube.com/watch?v=mVQiNpqXov8">an episode of her Layout Land</a> channel.</p>
<p>There are lots of ways that <code>minmax()</code> can be useful, but I want to demonstrate one way in particular. A lot of common layouts feature a “wrapper” grid that needs to fill the viewport (with some padding) up to a certain breakpoint (say, 1200px) and then grow no bigger. Content needs to occasionally bleed to the edge of the viewport, but in most cases align to this wrapper.</p>
<p>In terms of building a grid, what we need is:</p>
<ul>
<li>12 equal-width columns that take up a percentage of the available space, up to a maximum value (1200px (minus any gutters) divided by 12 (the number of columns)).</li>
<li>One flexible “padding” column either side with a minimum value of 20px, which after our arbitrary breakpoint will expand to fill the remaining space.</li>
</ul>
<p>Up until recently the way I’ve been coding these layouts is to set a breakpoint of just slightly over the wrapper width (plus padding columns) at which I change the values in my layout:</p>
<pre><code>.grid {
display: grid;
grid-template-columns: 20px repeat(12, 1fr) 20px;
@media (min-width: 1200px) {
grid-template-columns: 1fr repeat(12, $col) 1fr;
}
}
</code></pre>
<p>Before the breakpoint our layout consists on 12 flexible columns (using the <code>fr</code> unit) and two fixed “padding” columns. At a min-width breakpoint of 1200px I’m redefining the layout to have 12 fixed-width columns and two flexible padding columns. I could make this code more maintainable with CSS Variables (<a href="https://codepen.io/michellebarker/post/super-powered-layouts-with-css-variables-css-gr">as I’ve written elsewhere</a>), but in fact I’m not using CSS Grid to it’s full potential.</p>
<p>This also has some drawbacks: If I update any of my values, I need to make sure I adjust everything else accordingly. Plus if any of my calculations are slightly off, I get some undesirable effects happening around the breakpoint, where my grid columns actually take up more space than is available.</p>
<p>So now I realise that I’ve been creating unnecessary work for myself, what can I do about it?</p>
<p>By using <code>minmax()</code> smartly, I can actually do away with the media query altogether. I’ve tried to use <code>minmax()</code> to do a similar thing in the past, but without fully understanding that when I need my central columns (“tracks” in grid terminology) to be flexible, my outer columns need to be fixed, and vice versa. The key with a layout like this is to be explicit with when you want columns to be flexible and others to be fixed.</p>
<p>###Example 1</p>
<iframe height="332" scrolling="no" title="minmax() - Example 1" src="https://codepen.io/michellebarker/embed/YLNxPQ/?height=332&theme-id=0&default-tab=html,result&embed-version=2" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;">See the Pen <a href="https://codepen.io/michellebarker/pen/YLNxPQ/">minmax() - Example 1</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>This first example our columns grow flexibly (using the <code>fr</code> unit) but aren’t constrained by a maximum width, so they keep growing, no matter how wide the viewport gets. All of our columns are equal width, including the two padding columns:</p>
<pre><code>.grid {
display: grid;
grid-template-columns: repeat(14, 1fr);
grid-gap: 10px;
}
</code></pre>
<p>###Example 2</p>
<iframe height="336" scrolling="no" title="minmax() - Example 2" src="https://codepen.io/michellebarker/embed/GdrvJy/?height=336&theme-id=0&default-tab=html,result&embed-version=2" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;">See the Pen <a href="https://codepen.io/michellebarker/pen/GdrvJy/">minmax() - Example 2</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>Here I’m introducing <code>minmax()</code> to size our central grid tracks. By setting minimum of <code>auto</code> I can ensure the columns are wide enough for the content – empty columns will collapse ahead of ones that have content in:</p>
<pre><code>.grid {
display: grid;
grid-template-columns: 1fr repeat(12, minmax(auto, $col)) 1fr;
grid-gap: 10px;
}
</code></pre>
<p>####Auto vs. Zero</p>
<p>One thing to note is there is a difference between setting a min value of <code>auto</code> and a min value of <code>0</code>. In the following demo, while it may not be obvious at large viewport sizes, if you resize your browser you’ll see in the first of the two grids all of the columns collapse at the same rate, while in the second grid the first column remains wide enough to fit the content.</p>
<p data-height="340" data-theme-id="0" data-slug-hash="deNjdY" data-default-tab="html,result" data-user="michellebarker" data-embed-version="2" data-pen-title="CSS Grid minmax()" class="codepen">See the Pen <a href="https://codepen.io/michellebarker/pen/deNjdY/">CSS Grid minmax()</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.</p>
<script async="" src="https://static.codepen.io/assets/embed/ei.js"></script>
<p>Back to Example 2, if you resize the window you can see our padding tracks collapse to nothing. We want to maintain a minimum width for these columns so we need <code>minmax()</code> here too.</p>
<p>###Example 3</p>
<p>This is the layout we want:</p>
<iframe height="336" scrolling="no" title="minmax() - Example 3" src="https://codepen.io/michellebarker/embed/OZWjME/?height=336&theme-id=0&default-tab=html,result&embed-version=2" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;">See the Pen <a href="https://codepen.io/michellebarker/pen/OZWjME/">minmax() - Example 3</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>Here we’re specifying a minimum value of 20px for our padding columns and allowing them to grow (using the flexible <code>fr</code> unit) when space allows. At the same time we’re doing the opposite with our central columns, specifying that we want them to be flexible right up until they reach our calculated <code>$col</code> value, and then grow no larger.</p>
<pre><code>.grid {
display: grid;
grid-template-columns: minmax(20px, 1fr) repeat(12, minmax(auto, $col)) minmax(20px, 1fr);
}
</code></pre>
<p>When I realised this was a much simpler way of coding the layouts I’ve been building I definitely had a “d’oh” moment! But CSS Grid is so new and different to anything we’ve had in CSS before, and offers so much choice for constructing layout, that it really takes a lot of people using it in the real world to fully understand what is possible. I hope I can help make it simpler for others to use CSS Grid today!</p>
Using Pseudo-elements with CSS Grid2018-04-19T00:00:00Zhttps://css-irl.info/using-pseudo-elements/<p>This week I’ve had a couple of scenarios where I’ve needed to build a hero section with a full-width image, a large heading and a translucent sidebar overlaying the image – where the sidebar bleeds to the edge of the viewport but (crucially) the <em>content</em> of the sidebar aligns to what I like to call the “wrapper” grid columns – i.e. the columns of the grid where we actually want to place our content. (I have a whole other post almost written on this!)</p>
<p>This seems to be a fairly common occurrence with the designs that are coming my way these days, and it seems like a prime case for using pseudo-elements (<code>::before</code> or <code>::after</code>) as child items of the grid.</p>
<p>The markup for my grid looks like this:</p>
<pre><code><div class="grid">
<div class="grid__fig">
</div>
<div class="grid__heading">
<h1>CSS Layout News</h1>
</div>
<div class="grid__btn">
<a href="#0">Subscribe</a>
</div>
<aside class="grid__sidebar">
<ul class="sidebar__list">
<li class="sidebar__item">...</li>
<li class="sidebar__item">...</li>
<li class="sidebar__item">...</li>
</ul>
</aside>
</div>
</code></pre>
<p>The <code>div</code> with a class of <code>.grid</code> is, unsurprisingly, our parent grid container, which we need to give the property attribute <code>display: grid</code>.</p>
<pre><code>.grid {
@media (min-width: 800px) {
display: grid;
grid-template-columns: [start] minmax(20px, 1fr) [wrapper-start] repeat(8, var(--col)) [sidebar-start] repeat(4, var(--col)) [wrapper-end] minmax(20px, 1fr) [end];
grid-template-rows: minmax(3em, 1fr) auto minmax(auto, 1fr);
grid-gap: var(--gutter);
min-height: 100vh;
}
}
</code></pre>
<p>Here I’m using CSS Variables to make the code more flexible and maintainable – if you need a primer <a href="http://codepen.io/michellebarker/post/super-powered-layouts-with-css-variables-css-gr">I wrote a bit about them here</a>. I’m also naming my grid lines to make it easy to place my items.</p>
<p>We have three direct children of the grid container: The background image (<code>.grid__fig</code>), the heading and the sidebar, which can all be placed on the grid. The grid line <code>wrapper-end</code> is where I want the <em>content</em> of the sidebar to end, but the sidebar background needs to end at the the very edge of the viewport – the <code>end</code> grid line. Rather than placing the sidebar like this:</p>
<pre><code>.grid__sidebar {
grid-column: sidebar-start / end;
}
</code></pre>
<p>I can place it where I want the <em>content</em> to go:</p>
<pre><code>.grid__sidebar {
grid-column: span 3 / wrapper-end;
}
</code></pre>
<p>(Rather than adding another named grid line, which might make the <code>grid-template-columns</code> property start to get a bit long-winded and confusing – particularly if we have even more items we want to place – I’m just using <code>span 3</code> to indicate I want it to always span 3 columns, and <code>wrapper-end</code> as the line where I want it to end. It’s really useful to be able to switch the syntax around this way.)</p>
<p>Now I just need to create a pseudo-element for the sidebar background and place it on the grid. In order to act as a grid child item it needs to be a pseudo-element of the <em>grid container</em>, not of a grid child:</p>
<pre><code>.grid::after {
content: '';
display: block;
grid-column: sidebar-start / end;
grid-row: 1 / 4;
background-color: rgba(#f405ed, 0.5);
}
</code></pre>
<p>The sidebar background is now in front of the sidebar content, so we just need to tweak the <code>z-index</code> a little:</p>
<pre><code>.grid__sidebar {
grid-column: span 3 / wrapper-end;
...
z-index: 1;
}
</code></pre>
<p>Here’s the end result (a homage to the magnificent <a href="http://csslayout.news/">CSS Layout News</a>!):</p>
<p data-height="339" data-theme-id="0" data-slug-hash="VxYzyW" data-default-tab="css,result" data-user="michellebarker" data-embed-version="2" data-pen-title="CSS Grid – CSS Layout News example" class="codepen">See the Pen <a href="https://codepen.io/michellebarker/pen/VxYzyW/">CSS Grid – CSS Layout News example</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.</p>
<script async="" src="https://static.codepen.io/assets/embed/ei.js"></script>
<p>###Resources</p>
<p>As always, there are super smart people who have written about this stuff in-depth:</p>
<ul>
<li><a href="http://alistapart.com/article/faux-grid-tracks">Faux Grid Tracks</a> by Eric Meyer</li>
<li><a href="http://www.smashingmagazine.com/2018/02/generated-content-grid-layout/">Styling Empty Cells with Generated Content</a> by Rachel Andrew</li>
</ul>
Super-powered layouts with CSS Variables + CSS Grid2018-03-29T00:00:00Zhttps://css-irl.info/super-powered-layouts/<p>We’ve been using CSS Grid in production at <a href="http://ournameismud.co.uk/">Mud</a> for a few months now and I absolutely love the flexibility it’s giving me when coding layout. For so long we've got along without a true layout solution for the web - hacking it with floats and flexbox was just how we did things. But now, I honestly cannot imagine life without Grid!</p>
<p>One thing that’s only come to my full attention more recently is <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_variables">CSS Variables</a> (or Custom Properties). CSS Variables work a bit like variables in Sass and other preprocessors. The main difference is they’re compiled in the browser, unlike preprocessor variables, which are compiled into fixed CSS values before they hit the browser. CSS Variables are true dynamic variables that can be updated on the fly, either in your stylesheet or with JavaScript, which makes them super versatile. If you’re familiar with Javascript, I like to think of the difference between preprocessor variables and CSS Variables as similar to the difference between <code>const</code> and <code>let</code> - they both serve different purposes.</p>
<p>CSS Variables can have a whole host of useful applications (such as theming, for one). One way I’ve been looking at making use of CSS Variables recently is in layouts using CSS Grid where I need to redefine my <code>grid-template-rows</code> and <code>grid-template-columns</code> properties at different breakpoints. Here’s an example in the following pen, where I’ve used Sass variables to define my small and large column widths, which I’m passing into the <code>grid-template-rows</code> property. I’m doing the same with the <code>grid-gap</code> property, so that my gutters increase in size for each breakpoint:</p>
<iframe height="345" scrolling="no" title="Updating Grid track sizes Sass only" src="https://codepen.io/michellebarker/embed/JLprPb/?height=345&theme-id=0&default-tab=css,result&embed-version=2" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;">See the Pen <a href="https://codepen.io/michellebarker/pen/JLprPb/">Updating Grid track sizes Sass only</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>As you can see, I basically have to write out the entire block of code again within the media query in order to pass in the second variable, as the variable is fixed once defined. (I could of course use a mixin, but the net effect is the same - a bigger chunk of code.)</p>
<p>With CSS Variables I can down on the amount of code, as I can simply update my variable inside the media query and the browser recalculates my grid. Ten lines of (Sass) code may not seem like a huge saving, but the code is so much more readable - instead of having to add media queries in several places to deal with our new variables I can just declare them at the beginning of the code for that component and not have to worry about making sure I’ve replaced every value where it’s being used:</p>
<iframe height="338" scrolling="no" title="CSS Variables updating grid track sizes" src="https://codepen.io/michellebarker/embed/XEVoqJ/?height=338&theme-id=0&default-tab=css,result&embed-version=2" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;">See the Pen <a href="https://codepen.io/michellebarker/pen/XEVoqJ/">CSS Variables updating grid track sizes</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>One thing I’ve found with using CSS Grid is the syntax is quite verbose and it’s not always simple to see exactly what’s going on quickly and easily, especially with a complex grid. But using CSS Variables in this example I can set variables for the size and co-ordinates of Grid items and only write out the grid-column and grid-row properties once. This to me is a lot clearer than writing the full properties out every time, and very easy to see at a glance where we’re placing any grid item.</p>
<iframe height="342" scrolling="no" title="CSS Variables for randomly generated Grid placement" src="https://codepen.io/michellebarker/embed/zWEKQE/?height=342&theme-id=0&default-tab=js,result&embed-version=2" frameborder="no" allowtransparency="true" allowfullscreen="true" style="width: 100%;">See the Pen <a href="https://codepen.io/michellebarker/pen/zWEKQE/">CSS Variables for randomly generated Grid placement</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.
</iframe>
<p>Things get even more interesting when we add JavaScript into the mix! In the example above I’m using JavaScript to loop over the grid items and update the variables with a random value (within our grid parameters) each time the button is clicked. No added classes or extra CSS needed! (Please note, this is a work-in-progress proof-of-concept, don’t judge my JS ;) )</p>
<p>In the example below I’m using user inputs to dynamically change our grid items. All that's getting updated here are the three variables for the x and y coordinates and the size of the grid item.</p>
<p data-height="336" data-theme-id="0" data-slug-hash="xWPyWj" data-default-tab="js,result" data-user="michellebarker" data-embed-version="2" data-pen-title="CSS Variables + CSS Grid experiment (in progress)" class="codepen">See the Pen <a href="https://codepen.io/michellebarker/pen/xWPyWj/">CSS Variables + CSS Grid experiment (in progress)</a> by Michelle Barker (<a href="https://codepen.io/michellebarker">@michellebarker</a>) on <a href="https://codepen.io/">CodePen</a>.</p>
<script async="" src="https://static.codepen.io/assets/embed/ei.js"></script>
<p>So many possibilities!</p>
<h3>Browser support</h3>
<p>At the moment CSS Variables are supported in 88% of browsers worldwide – Internet Explorer 11 and below being the obvious exception. That’s roughly the same as support for Grid Layout, which means it’s fairly simple to test for support using feature queries.</p>
<p>You can use an <code>@supports</code> declaration like this to test for support for CSS Variables:</p>
<pre><code> @supports(--css: variables) {
.my-div {
--size: 2;
--posX: 3;
grid-column: var('--posX') / span var('--size');
}
}
</code></pre>
<p>(See <a href="https://codepen.io/SitePoint/pen/zzBrWY">this example pen</a> by SitePoint)</p>
<p>I hope this gives you a little taste of what's possible with CSS Variables!</p>
How to be a Good Co-worker2016-04-17T00:00:00Zhttps://css-irl.info/how-to-be-a-good-co-worker/<p>Transitioning to a new team inevitably involves learning new methods and processes – as well as getting used to the personalities of your co-workers, and where you fit in the pecking order! My first two weeks as a Junior Front-end Developer at Mud have been fun, interesting and very challenging at times. So I thought I’d share some of my observations, as well as lessons learnt from some of the previous teams I’ve been involved with.</p>
<h3>Be nice to the newb</h3>
<p>Every workplace has a different dynamic, and different processes and procedures to get used to. Front-end teams in particular are (necessarily) very process-driven, and on the first day at a new job it’s easy to feel overwhelmed with all the new things to learn. I joined the Mud team from a previous position as an in-house designer, where my job involved designing for both web and print. Although I was used to designing in HTML and CSS, I knew there would be lots of new stuff to get to grips with as I transitioned to a more technical role in an agency environment. I often had to remind myself that I wasn’t expected to know it all by the end of my first day, and that I could always ask my colleagues for help.</p>
<p>Empathy and patience from your co-workers are vital for making any new hire feel welcome, and luckily my new colleagues have been understanding and helpful as I’ve settled in. Allowing a new team member to learn at their own pace, without imposing immediate deadlines, helps them feel relaxed and able to take in more information – trying to take everything on board while simultaneously feeling stressed is unhelpful for everyone.</p>
<h3>If you are the newb...watch and learn</h3>
<p>When joining a new team, taking notes is useful (particularly of repetitive procedures – so you don’t have to keep asking your colleagues), but with so much information to take in, the best way is often to learn by doing – and by watching other developers. Important too is knowing when to ask for help and when to spend the time figuring something out for yourself. It’s a balance between saving time or ultimately learning more by working it out – and trying not to annoy your busy co-workers with constant questions!</p>
<h3>Use your tools</h3>
<p>When working with remote teams (as I often was in my previous role), communication and documentation become even more important. Here are some of the tools that may be useful to keep you all on track:</p>
<h4>Slack</h4>
<p>This is great for quick chats or message, especially when you either can’t be in the same room as your colleagues, or don’t want to disturb others. You can also share files quickly and easily.</p>
<h4>Git</h4>
<p>Pretty darn useful for version control, and absolutely vital if you have more than one person pushing code!</p>
<h4>InVision</h4>
<p>A great tool for giving feedback on design and front-end work. You can upload a screenshot, add comments and mark when completed, which was far more effective than logging a huge list of issues in a spreadsheet. In addition, you can create interactive prototypes and a lot more.</p>
<h4>Teamwork</h4>
<p>At Mud we use Teamwork to keep track of projects, but there are tonnes of other project management tools out there that do similar things, so you can pick whichever one works for you. I find it very useful to make lists of any tasks I need to work on within a project, even if they’re just minor tweaks.</p>
<h4>Gulp</h4>
<p>Whatever the size of your team, some sort of front-end automation will save bucketloads of time. Gulp is the Mud tool of choice.</p>
<h4>FreeAgent</h4>
<p>Useful for tracking the time spent on various projects when you have lots on the go.</p>
<h4>Google Drive</h4>
<p>Pretty much all our team documentation in my previous job was in Google Drive, which is an easy way of sharing between teams. Sometimes a good, old-fashioned spreadsheet is all you really need.</p>
<h3>In the words of Coldplay: Let’s Talk*^</h3>
<p>...but don’t just use tools.</p>
<p>Fostering a culture of communication is just as important, and there’s no substitute for face-to-face time. Taking conversations offline and getting to know each other in real life helps keep everyone happy – while emails and brief messages can often be misread (especially in stressful situations), and while you could spend all morning trying to phrase something in an email, a quick chat could get to the root of the issue straight away.</p>
<p>In remote teams this takes a bit more work but, whether you schedule Skype meetings a few times a week, jump onto calls as and when needed, or all get together face-to-face for a big blowout once in a while, good personal relationships often make for better working relationships.</p>
<h3>Minimise meetings</h3>
<p>That said, no one wants to sit in meetings for hours when there are plenty more pressing things you could be doing! Keep meetings short and to-the-point, with the aim of setting clear objectives, and your colleagues will thank you for it.</p>
<h4>So...</h4>
<p>In conclusion, a good working culture will not only attract the best employees, but will help you hang onto your valuable existing ones. And with so many tools at our fingertips, it’s arguably never been easier. But it’s not just about management – everyone can play a part in making a great team!</p>
<p>*There are probably far better musical references I could’ve used here.</p>
<p>^ Coldplay reference used without permission of Mud CD</p>