<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en" xml:base="https://pepelsbey.dev"><title>Vadim Makeev</title><subtitle>Frontend developer in love with the Web, browsers, bicycles, and podcasting</subtitle><link href="https://pepelsbey.dev"/><link href="https://pepelsbey.dev/feed/" rel="self"/><updated>2026-04-06T22:01:46Z</updated><id>https://pepelsbey.dev/</id><author><name>Vadim Makeev</name><email>hi@pepelsbey.dev</email></author><entry><title>Web Standards. Daily web platform news</title><link href="https://pepelsbey.dev/articles/web-standards-news/"/><updated>2026-02-11T00:00:00Z</updated><id>https://pepelsbey.dev/articles/web-standards-news/</id><content type="html">&lt;p&gt;This year the &lt;a href=&quot;https://www.youtube.com/webstandards_ru&quot;&gt;Web Standards&lt;/a&gt; podcast for the Russian-speaking community celebrated 10 years. You might’ve spotted the podcast in the &lt;a href=&quot;https://2025.stateofjs.com/en-US/resources/#podcasts&quot;&gt;State of JavaScript 2025&lt;/a&gt; results and wondered what it’s doing there. For almost as long, we were publishing daily news on the web platform: browser releases, spec changes, useful articles, tools. Thousands of links over the years!&lt;/p&gt;
&lt;p&gt;I always wanted to do something like this for the English-speaking community: &lt;strong&gt;one piece of news a day, every weekday, with a short summary and a cute cover&lt;/strong&gt;. The kind of thing that would keep you informed even if you don’t have a lot of time to follow the news.&lt;/p&gt;
&lt;p&gt;Well, the time has come. On September 9, 2025, I published the &lt;a href=&quot;https://web-standards.dev/news/2025/09/liquid-glass-css-svg/&quot;&gt;first news&lt;/a&gt; on &lt;a href=&quot;https://web-standards.dev/&quot;&gt;Web Standards&lt;/a&gt;, a new project dedicated to daily web platform news in English. And yesterday, February 10, 2026, I hit a milestone: &lt;a href=&quot;https://web-standards.dev/news/2026/02/reference-target-for-shadow-hosts/&quot;&gt;the 100th news&lt;/a&gt; 🎉&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://web-standards.dev/&quot;&gt;&lt;img src=&quot;https://pepelsbey.dev/articles/web-standards-news/images/home@light.png&quot; alt=&quot;The Web Standards homepage with news cards showing cover images, dates, titles, and summaries. The archive link in the corner says 100.&quot; /&gt;&lt;/a&gt;
It took five months to reach 100 news.&lt;/p&gt;
&lt;h2&gt;What’s inside&lt;/h2&gt;
&lt;p&gt;Every news item is a link to an article, announcement, or release, paired with a short summary. I try to make it useful on its own: you should get the gist even if you don’t click through. I also create a cover image for each one to make it stand out in social feeds. It’s a small thing, but it helps attract attention in a busy timeline.&lt;/p&gt;
&lt;p&gt;I try to cover all major browser releases, surveys, practical tutorials, and tools. Basically, if it matters for the web platform, it’ll probably show up. I implemented a system of tags and a quick search to help navigate the growing archive. The main page features five tags: &lt;a href=&quot;https://web-standards.dev/tags/html/&quot;&gt;HTML&lt;/a&gt;, &lt;a href=&quot;https://web-standards.dev/tags/css/&quot;&gt;CSS&lt;/a&gt;, &lt;a href=&quot;https://web-standards.dev/tags/js/&quot;&gt;JavaScript&lt;/a&gt;, &lt;a href=&quot;https://web-standards.dev/tags/a11y/&quot;&gt;accessibility&lt;/a&gt;, and &lt;a href=&quot;https://web-standards.dev/tags/browser/&quot;&gt;browsers&lt;/a&gt;. Even though they’re called “news,” most of them stay relevant for many months or even years. A good tutorial on CSS grid doesn’t expire next week.&lt;/p&gt;
&lt;h2&gt;How it works&lt;/h2&gt;
&lt;p&gt;The site is built on &lt;a href=&quot;https://www.11ty.dev/&quot;&gt;Eleventy&lt;/a&gt;, because of course it is 😎&lt;/p&gt;
&lt;p&gt;I prepare news in advance and mark them with a publication date and &lt;code&gt;draft: true&lt;/code&gt;. The rest is handled by a &lt;a href=&quot;https://github.com/web-standards/web-standards.dev/blob/main/.github/workflows/publish.yml&quot;&gt;GitHub Actions workflow&lt;/a&gt; that runs every weekday at 11:00 UTC. It checks if there’s a news item scheduled for today, removes the draft flag, commits, and pushes. The site rebuilds automatically. I don’t have to touch anything on the day of publication.&lt;/p&gt;
&lt;p&gt;Social posting is still manual: I cross-post every news to &lt;a href=&quot;https://mastodon.social/@webstandards_dev&quot;&gt;Mastodon&lt;/a&gt;, &lt;a href=&quot;https://bsky.app/profile/web-standards.dev&quot;&gt;Bluesky&lt;/a&gt;, and &lt;a href=&quot;https://x.com/webstandardsdev&quot;&gt;X&lt;/a&gt;. It gives me a chance to tweak the message for each platform, but honestly, I’m considering automating this part too.&lt;/p&gt;
&lt;h2&gt;What’s next&lt;/h2&gt;
&lt;p&gt;I’m thinking about starting an email list with a weekly digest. Not everyone reads RSS these days or wants to follow news on socials, and a short weekly email with five links feels like a nice format. If that sounds interesting to you, stay tuned.&lt;/p&gt;
&lt;p&gt;In the meantime, you can subscribe to the &lt;a href=&quot;https://web-standards.dev/feed/&quot;&gt;RSS feed&lt;/a&gt;, browse the &lt;a href=&quot;https://web-standards.dev/news/&quot;&gt;archive&lt;/a&gt;, or check out the &lt;a href=&quot;https://github.com/web-standards/web-standards.dev&quot;&gt;source code&lt;/a&gt; if you’re curious about the setup. And if you know a good article or tool that deserves a mention, let me know!&lt;/p&gt;
&lt;p&gt;Here’s to the next hundred ✨&lt;/p&gt;
			</content></entry><entry><title>Native HTML light and dark color scheme switching</title><link href="https://pepelsbey.dev/articles/native-light-dark/"/><updated>2025-01-06T00:00:00Z</updated><id>https://pepelsbey.dev/articles/native-light-dark/</id><content type="html">&lt;p&gt;It’s getting dark early in Berlin in the winter. It’s not even close to evening, but my OS and all apps have already switched to dark mode. Well, not all of them, unfortunately. And that’s the thing: dark mode has become a quality-of-life feature for many users, and I often try to avoid using apps or websites that haven’t implemented it, especially in the evening. They literally hurt my eyes!&lt;/p&gt;
&lt;p&gt;When it comes to color scheme implementations, they range from rather useless ones that require a page reload to more sensible ones that query the &lt;code&gt;prefers-color-scheme&lt;/code&gt; media feature and apply changes in CSS on the fly:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	background-color&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;#ffffff&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	color&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;#000000&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	@&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;media&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;prefers-color-scheme&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;dark&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;		background-color&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;#000000&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;		color&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;#ffffff&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;⭐ I’ll be using &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_nesting/Using_CSS_nesting&quot;&gt;native CSS nesting&lt;/a&gt; in all demos throughout this article. It works in all modern browsers and makes code a bit more compact, especially when it comes to media queries. But if you’re not familiar with CSS nesting, you can use this handy &lt;a href=&quot;https://lightningcss.dev/playground/#%7B%22minify%22%3Afalse%2C%22customMedia%22%3Atrue%2C%22cssModules%22%3Afalse%2C%22analyzeDependencies%22%3Afalse%2C%22targets%22%3A%7B%22chrome%22%3A6225920%7D%2C%22include%22%3A0%2C%22exclude%22%3A0%2C%22source%22%3A%22body%20%7B%5Cn%20%20background-color%3A%20%23000000%3B%5Cn%20%20color%3A%20%23ffffff%3B%5Cn%20%20%5Cn%20%20%40media%20(prefers-color-scheme%3A%20dark)%20%7B%5Cn%20%20%20%20background-color%3A%20%23000000%3B%5Cn%20%20%20%20color%3A%20%23ffffff%3B%5Cn%20%20%7D%5Cn%7D%22%2C%22visitorEnabled%22%3Afalse%2C%22visitor%22%3A%22%7B%5Cn%20%20Color(color)%20%7B%5Cn%20%20%20%20if%20(color.type%20%3D%3D%3D%20&#39;rgb&#39;)%20%7B%5Cn%20%20%20%20%20%20color.g%20%3D%200%3B%5Cn%20%20%20%20%20%20return%20color%3B%5Cn%20%20%20%20%7D%5Cn%20%20%7D%5Cn%7D%22%2C%22unusedSymbols%22%3A%5B%5D%2C%22version%22%3A%22local%22%7D&quot;&gt;Lightning CSS playground&lt;/a&gt; to figure out how it looks without nesting.&lt;/p&gt;
&lt;p&gt;This approach is already a good start. But it only covers the simplest case and doesn’t allow users to choose a different color scheme for this specific website. Just like light color schemes hurt my eyes in the evening, many people are not comfortable with dark schemes or with particular ones that aren’t good for them. So, it’s all about user choice.&lt;/p&gt;
&lt;p&gt;Currently, there’s no way to directly override a user’s OS preference if you want to offer a scheme selector on your page. Fortunately, in the CSS Media Queries Level 5 spec, there’s a &lt;a href=&quot;https://drafts.csswg.org/mediaqueries-5/#auto-pref%E2%91%A0&quot;&gt;&lt;code&gt;PreferenceManager&lt;/code&gt; interface&lt;/a&gt; that will solve this problem. Meanwhile, the most popular solution these days is to use JavaScript to set an attribute like &lt;code&gt;&amp;lt;html data-scheme=&amp;quot;dark&amp;quot;&amp;gt;&lt;/code&gt; reflecting the forced scheme and use it in CSS:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	background-color&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;#ffffff&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	color&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;#000000&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	[&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;data-scheme&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;dark&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&#39;] &amp;#x26; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;		background-color&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;#000000&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;		color&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;#ffffff&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This approach always seemed hacky to me. However, if you know HTML well enough, there are a few much more convenient native options available.&lt;/p&gt;
&lt;h2&gt;Setting color-scheme&lt;/h2&gt;
&lt;p&gt;If you ever fall into the dark scheme rabbit hole, the first thing you’ll learn is the &lt;code&gt;color-scheme&lt;/code&gt; CSS property. It’s essential for setting the scene for everything else. Most importantly, it turns on the browser’s default dark scheme support. Which, unfortunately, browsers can’t enable by default for backward compatibility reasons.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;:root&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	color-scheme&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;light&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; dark&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;light dark&lt;/code&gt; value means that we’re choosing to support both light and dark schemes in our code. The property will be inherited down the document tree, and the browser will enable some default styling for built-in primitives when needed. Thank you, browser!&lt;/p&gt;
&lt;figure style=&quot;margin-bottom: 2.4rem&quot;&gt;
&lt;p&gt;&lt;img src=&quot;https://pepelsbey.dev/articles/native-light-dark/images/color-scheme.png&quot; alt=&quot;Two browser windows with the same page: one in the light scheme, the other in the dark scheme. The text on the page says: link is not a button! The link word is a link, the button word is a button. All page elements, including scrollbars, are perfectly aligned with the scheme.&quot; /&gt;&lt;/p&gt;
&lt;figcaption&gt;
	Default browser dark styles enabled by the &lt;code&gt;color-scheme: light dark&lt;/code&gt; property.
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;When switching your schemes, it’s important to switch the value of this property, too: set &lt;code&gt;color-scheme: light&lt;/code&gt; on the root along with light styles and the other way around for the dark ones. Remember this, it’ll come in handy later.&lt;/p&gt;
&lt;p&gt;And somewhere along these lines, you’ll probably read that you can also set this mode right in HTML using the &lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt; element. Why would you do that? Oh well, who knows? Maybe you don’t use CSS or whatever. So silly, right?&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;meta&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; name&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;color-scheme&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; content&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;light dark&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It turns out that it’s not just a flag for the browser but a tool you can use to force color schemes using JavaScript! But only if you use the fairly new &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/light-dark&quot;&gt;&lt;code&gt;light-dark()&lt;/code&gt;&lt;/a&gt; CSS function.&lt;/p&gt;
&lt;h2&gt;Switching color-scheme&lt;/h2&gt;
&lt;p&gt;Remember the earlier example with media queries and &lt;code&gt;‌prefers-color-scheme&lt;/code&gt;?&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	background-color&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;#ffffff&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	color&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;#000000&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	@&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;media&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;prefers-color-scheme&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;dark&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;		background-color&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;#000000&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;		color&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;#ffffff&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can also express the same idea like this:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	background-color&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: light-dark(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;#ffffff&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;#000000&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	color&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: light-dark(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;#000000&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;#ffffff&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;⭐ By the way, I use &lt;code&gt;--color-back&lt;/code&gt; and &lt;code&gt;--color-text&lt;/code&gt; variables in my demos. It makes it easier to set colors even in a small demo, let alone a bigger project. But to make things easier to read, I chose to set colors directly in code samples.&lt;/p&gt;
&lt;p&gt;Here’s the three-position switch I often use. Along with “light” and “dark” options that force a certain scheme, there’s also the “auto” option that gives the control over the color scheme back to the OS, selected by default.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;section&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; aria-label&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;Color scheme switcher&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; aria-pressed&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;false&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; value&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;light&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		Light&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; aria-pressed&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; value&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;light dark&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		Auto&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; aria-pressed&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;false&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; value&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;dark&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		Dark&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;section&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And to make it all work, a simple script that takes the button’s value and sets it to the &lt;code&gt;&amp;lt;meta name=&amp;quot;color-scheme&amp;quot;&amp;gt;&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; colorScheme&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; document.&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;querySelector&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;meta[name=color-scheme]&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; switchButtons&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; document.&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;querySelectorAll&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;button&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;switchButtons.&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;forEach&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FFAB70;--shiki-light:#E36209&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	button.&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;addEventListener&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;click&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;, () &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;		const&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; currentButton&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; button;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		switchButtons.&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;forEach&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#FFAB70;--shiki-light:#E36209&quot;&gt;			button&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt; =&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; button.&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;setAttribute&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;				&#39;aria-pressed&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;, button &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;===&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; currentButton&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;			)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		colorScheme.content &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; button.value;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, once we set &lt;code&gt;content=&amp;quot;dark&amp;quot;&lt;/code&gt; the browser switches to the last value in the &lt;code&gt;light-dark()&lt;/code&gt; function and the other way around with the &lt;code&gt;light&lt;/code&gt; one. This HTML’s &lt;code&gt;color-scheme&lt;/code&gt; turned out not so silly after all!&lt;/p&gt;
&lt;p&gt;To make it work properly, you’ll need to decide where to store your global &lt;code&gt;color-scheme&lt;/code&gt; value, so the script can force it. In this example, I chose to use the HTML one, so I removed the &lt;code&gt;color-scheme&lt;/code&gt; property from CSS. But you can also keep it in CSS and force it via JavaScript like so:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;html&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; style&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;color-scheme: dark&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;iframe src=&quot;https://pepelsbey.dev/articles/native-light-dark/demo/color-scheme/index.html&quot; height=&quot;256&quot; loading=&quot;lazy&quot; title=&quot;Three-button switcher group: light, auto, dark. The auto button is selected.&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;⭐ For a good UX, you’ll also need a way to store user preference somewhere in &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage&quot;&gt;&lt;code&gt;localStorage&lt;/code&gt;&lt;/a&gt;, so the users won’t have to switch it next time they visit or when opening a new tab. I’m sure you can figure it out on your own!&lt;/p&gt;
&lt;p&gt;One of the downsides of this approach is the browser support: the &lt;code&gt;light-dark()&lt;/code&gt; CSS function has been available in all modern browsers since May 2024, which makes it “newly available” on the &lt;a href=&quot;https://web-platform-dx.github.io/web-features/&quot;&gt;Baseline&lt;/a&gt; scale. It will become “widely available” only around November 2026 or 30 months later. You can transpile it for older browsers using &lt;a href=&quot;https://lightningcss.dev/&quot;&gt;Lightning CSS&lt;/a&gt; or &lt;a href=&quot;https://github.com/csstools/postcss-plugins/tree/main/plugins/postcss-light-dark-function&quot;&gt;PostCSS plugin&lt;/a&gt;, but make sure you check the output and test it in an older browser. It might be a bit tricky at times.&lt;/p&gt;
&lt;p&gt;As for the other major downside, the &lt;code&gt;light-dark()&lt;/code&gt; function accepts only colors for now. Trust me, I tried to use it with other values, and it didn’t work. So, what’s the problem? You might need to change not just colors but images or font properties that work better with the dark scheme.&lt;/p&gt;
&lt;p&gt;In this case, there’s another HTML solution for you!&lt;/p&gt;
&lt;h2&gt;Linking a CSS scheme&lt;/h2&gt;
&lt;p&gt;You all know the most common way of linking CSS to a page: it’s a stylesheet linked from a file.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;link&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; rel&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;index.css&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But did you know that you can also set the &lt;code&gt;media&lt;/code&gt; attribute to conditionally load and apply CSS based on user preferences? Sure you can! But first, you’d need to split your files into light and dark styles:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;link&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	rel&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;light.css&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	media&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;(prefers-color-scheme: light)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;link&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	rel&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;dark.css&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	media&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;(prefers-color-scheme: dark)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It doesn’t look too convenient this way. A much better approach would be to split your styles into &lt;em&gt;three&lt;/em&gt; parts! Yes, I’m serious. Hear me out!&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The main file, called &lt;code&gt;index.css&lt;/code&gt;, will contain all your styles and use CSS variables for anything you need to change depending on a color scheme.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;light.css&lt;/code&gt; file will contain only CSS variables with values set to everything that makes sense for the light scheme.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;dark.css&lt;/code&gt;, you guessed it, will have everything dark.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Remember to set the appropriate &lt;code&gt;color-scheme&lt;/code&gt; property values in each color scheme file to help the browser: &lt;code&gt;color-scheme: light&lt;/code&gt; and &lt;code&gt;color-scheme: dark&lt;/code&gt;, respectfully.&lt;/p&gt;
&lt;p&gt;Interestingly enough, the file that doesn’t fit user preferences will still be loaded by the browser but with lower priority. I went into much greater detail about this in my other “&lt;a href=&quot;https://pepelsbey.dev/articles/conditionally-adaptive/&quot;&gt;Condi­tionally adaptive CSS&lt;/a&gt;” article if you’re curious.&lt;/p&gt;
&lt;p&gt;So, this three-file CSS architecture now has all the inconveniences and no benefits compared to the previously discussed solution with media queries. What now? Don’t you worry, it was just the first step. Now, to the switching.&lt;/p&gt;
&lt;h2&gt;Switching CSS schemes&lt;/h2&gt;
&lt;p&gt;When you’re linking your much simpler single-file styles, there’s something else you’re &lt;em&gt;implicitly&lt;/em&gt; setting: the &lt;code&gt;media&lt;/code&gt; attribute. If it’s not &lt;em&gt;explicitly&lt;/em&gt; set, it means that you want your styles to apply to all &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/@media#media_types&quot;&gt;media types&lt;/a&gt;. So, it defaults to &lt;code&gt;all&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;link&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; rel&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;index.css&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; media&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;all&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://www.filamentgroup.com/lab/load-css-simpler/&quot;&gt;There are techniques&lt;/a&gt; using &lt;code&gt;media=&amp;quot;print&amp;quot;&lt;/code&gt; for lazy-loading CSS, but since we’ve learned that the &lt;code&gt;media&lt;/code&gt; attribute can take not only media types but complex media queries, you can negate it as &lt;code&gt;not all&lt;/code&gt; and it won’t be applied to any media.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;link&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; rel&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;index.css&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; media&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;not all&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But why would you need &lt;em&gt;not&lt;/em&gt; to load your styles? Now you see where it’s going. We can use it to switch between color schemes and force user preferences.&lt;/p&gt;
&lt;p&gt;Let’s take the same three-position switch from the previous example but change the auto button’s value to &lt;code&gt;auto&lt;/code&gt; to better match what it does. Our JavaScript function becomes a bit bigger, but the idea stays the same. The only difference this time is that we’re changing the &lt;code&gt;media&lt;/code&gt; attribute’s value:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; styleLight&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; document.&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;querySelector&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;link[rel=stylesheet][media*=prefers-color-scheme][media*=light]&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; styleDark&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; document.&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;querySelector&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;link[rel=stylesheet][media*=prefers-color-scheme][media*=dark]&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;function&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; switchScheme&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FFAB70;--shiki-light:#E36209&quot;&gt;scheme&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;	let&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; lightMedia;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;	let&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; darkMedia;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;	if&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; (scheme &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;===&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt; &#39;auto&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		lightMedia &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt; &#39;(prefers-color-scheme: light)&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		darkMedia &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt; &#39;(prefers-color-scheme: dark)&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	} &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;else&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		lightMedia &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; (scheme &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;===&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt; &#39;light&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt; &#39;all&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt; :&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt; &#39;not all&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		darkMedia &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; (scheme &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;===&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt; &#39;dark&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;?&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt; &#39;all&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt; :&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt; &#39;not all&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	styleLight.media &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; lightMedia;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	styleDark.media &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; darkMedia;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In short, if we force the dark scheme, the &lt;code&gt;dark.css&lt;/code&gt; gets &lt;code&gt;media=&amp;quot;all&amp;quot;&lt;/code&gt; instead of the &lt;code&gt;prefers-color-scheme&lt;/code&gt; and the &lt;code&gt;light.css&lt;/code&gt; one gets &lt;code&gt;‌media=&amp;quot;not all&amp;quot;&lt;/code&gt;, and the other way around for the light scheme. Once the user chooses the “auto” option, we stop forcing and restoring all previous &lt;code&gt;prefers-color-scheme&lt;/code&gt; media values.&lt;/p&gt;
&lt;iframe src=&quot;https://pepelsbey.dev/articles/native-light-dark/demo/link-media/index.html&quot; height=&quot;256&quot; loading=&quot;lazy&quot; title=&quot;Three-button switcher group: light, auto, dark. The auto button is selected.&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;Given that scheme files containing only variables are relatively small, and browsers download all CSS files anyway (only the priority differs), the switching happens seamlessly. You can check this method in action on this website. And if you happen to use Safari or Vivaldi browsers, you might notice something else changing while you switch the schemes.&lt;/p&gt;
&lt;h2&gt;One more thing&lt;/h2&gt;
&lt;p&gt;There’s the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Manifest/theme_color&quot;&gt;&lt;code&gt;theme_color&lt;/code&gt;&lt;/a&gt; key in the web manifest that sets the installed app’s chrome color. You can also set it via HTML and even use the &lt;code&gt;media&lt;/code&gt; attribute to apply different theme colors depending on the color scheme:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;meta&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	name&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;theme-color&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	content&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;#c1f07c&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	media&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;(prefers-color-scheme: light)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;meta&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	name&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;theme-color&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	content&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;#9874d3&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	media&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;(prefers-color-scheme: dark)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I bet you saw that coming: you can also force the theme color while switching the color scheme using &lt;code&gt;all&lt;/code&gt; and &lt;code&gt;not all&lt;/code&gt; values via the same script. How cool is that?&lt;/p&gt;
&lt;figure style=&quot;margin-bottom: 2.4rem&quot;&gt;
&lt;p&gt;&lt;img src=&quot;https://pepelsbey.dev/articles/native-light-dark/images/theme-color.png&quot; alt=&quot;Two Safari windows: one in the light scheme, the other in the dark scheme. The light one has a lime top panel, the dark one has a violet top panel.&quot; /&gt;&lt;/p&gt;
&lt;figcaption&gt;
	Different top panel colors using the &lt;code&gt;theme-color&lt;/code&gt; meta element.
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;h2&gt;Homework&lt;/h2&gt;
&lt;p&gt;As all of us did, I learned most of what I know from other people’s posts and articles. I want to say a special thank you to Thomas Steiner for his early articles on color scheme switching: &lt;a href=&quot;https://web.dev/articles/prefers-color-scheme#dark-mode-but-add-an-opt-out&quot;&gt;Prefers-color-scheme: Hello darkness, my old friend&lt;/a&gt; and &lt;a href=&quot;https://web.dev/articles/color-scheme&quot;&gt;Improved dark mode default styling with the color-scheme&lt;/a&gt;. Please also have a look at Darin Senneff’s &lt;a href=&quot;https://www.darins.page/articles/progressively-enhanced-dark-mode&quot;&gt;Progressively-enhanced dark mode&lt;/a&gt; and Sara Joy’s &lt;a href=&quot;https://css-tricks.com/come-to-the-light-dark-side/&quot;&gt;Come to the light-dark() side&lt;/a&gt; articles for a different perspective on the matter. And let’s dream about a better future for color scheme switching while reading Bramus’ &lt;a href=&quot;https://www.bram.us/2024/04/13/what-if-you-had-real-control-over-light-mode-dark-mode-on-a-per-site-basis/&quot;&gt;What if you had real control over light mode / dark mode on a per-site basis?&lt;/a&gt; article.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;Initially published in the &lt;a href=&quot;https://htmhell.dev/adventcalendar/2024/9/&quot;&gt;HTMHell advent calendar&lt;/a&gt; in December 2024. Thank you to &lt;a href=&quot;https://www.matuzo.at/&quot;&gt;Manuel Matuzovic&lt;/a&gt;, &lt;a href=&quot;https://saptaks.website/&quot;&gt;Saptak Sengupta&lt;/a&gt;, and &lt;a href=&quot;https://stolley.dev/&quot;&gt;Karl Stolley&lt;/a&gt; for proofreading and feedback.&lt;/em&gt;&lt;/p&gt;
			</content></entry><entry><title>The road to HTMHell is paved with semantics</title><link href="https://pepelsbey.dev/articles/road-to-htmhell/"/><updated>2024-01-08T00:00:00Z</updated><id>https://pepelsbey.dev/articles/road-to-htmhell/</id><content type="html">&lt;p&gt;HTML semantics is a nice idea, but does it really make a difference? There’s a huge gap between HTML spec’s good intentions and what browsers and screen readers are willing to implement. Writing semantic markup only because &lt;a href=&quot;https://youtu.be/EIBRdBVkDHQ&quot;&gt;the good spec is a spec, and it is good, and it’s a spec&lt;/a&gt; is not the worst approach you can take, but it might lead you to HTMHell.&lt;/p&gt;
&lt;h2&gt;Simple days&lt;/h2&gt;
&lt;p&gt;Like most people involved in front-end, I started my journey into Web development with HTML. It was simple enough, close to a natural language, and easy to use: you type some tags, save a text file, and reload the browser to see the result. And it would almost never fail if I made a mistake!&lt;/p&gt;
&lt;p&gt;Back then, I considered HTML a simple set of visual building blocks. It was too late for purely visual &lt;code&gt;&amp;lt;font&amp;gt;&lt;/code&gt; elements (the CSS has replaced them), but the general idea stayed pretty much the same: if you wrap your text into &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt;, it becomes big and bold, if you have two &lt;code&gt;&amp;lt;td&amp;gt;&lt;/code&gt; cells in a row, that’s your two-column layout. Easy! I learned tags to be able to achieve certain styles and behaviors. Remember &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/marquee&quot;&gt;&lt;code&gt;&amp;lt;marquee&amp;gt;&lt;/code&gt;&lt;/a&gt;?&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;marquee&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	behavior&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;alternate&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	scrollamount&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;7&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;marquee&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That was just the beginning: soon, I needed calendars, popups, icons, etc. It turned out I had to code them myself! And so I did, mainly using divs, spans, and some CSS. Back in the mid-2000s, there weren’t any particular “logical” tags or functional widgets, only the ones you’d find on a typical text editor panel.&lt;/p&gt;
&lt;p&gt;But at some point, a trend called “&lt;a href=&quot;https://www.webstandards.org/&quot;&gt;web standards&lt;/a&gt;” emerged: it suggested to stop using HTML as a set of visual blocks and start thinking about the meaning of the content and wrapping it into appropriate tags: &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt; only for tabular data, not layout; &lt;code&gt;&amp;lt;blockquote&amp;gt;&lt;/code&gt; only for quotes, not indentation, etc. The people bringing the web standards gospel were convincing enough, so I joined the movement.&lt;/p&gt;
&lt;h2&gt;Semantics&lt;/h2&gt;
&lt;p&gt;Following the trend, we started studying the HTML 4 spec to learn the proper meaning of all those tags we’ve already known and many new ones we’ve never heard about. Suddenly, we’ve discovered semantics in HTML, not just visual building blocks.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;b&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;i&amp;gt;&lt;/code&gt; weren’t cool anymore: the proper stress and emphasis could only be achieved with &lt;code&gt;&amp;lt;strong&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;em&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;ol&amp;gt;&lt;/code&gt; weren’t only for bulleted/numbered lists in content anymore, but for all kinds of UI lists: menus, cards, icons.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;dl&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;dt&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;dd&amp;gt;&lt;/code&gt; were accidentally discovered in the spec and extensively used for all kinds of lists with titles.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt; was banned from layout usage mainly because it wasn’t meant for that by the spec, but later, we also discovered rendering performance reasons.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Why? Because we started paying attention to the spec, and it was semantically correct to do so. Every decision we make would have to be checked to determine whether it’s semantic enough. And how would we do that? By reading the spec like it’s a holy book that gives you answers in challenging moments of your life. On top of that, there was the &lt;a href=&quot;https://validator.w3.org/&quot;&gt;HTML Validator&lt;/a&gt;’s seal of approval.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pepelsbey.dev/articles/road-to-htmhell/images/valid.svg&quot; alt=&quot;W3C HTML 4.01 badge with a checkmark.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;But then came the &lt;a href=&quot;https://en.wikipedia.org/wiki/Cambrian_explosion&quot;&gt;Cambrian explosion&lt;/a&gt; that changed everything: HTML 5.&lt;/p&gt;
&lt;h2&gt;A new hope&lt;/h2&gt;
&lt;p&gt;Just after the failed promise of &lt;a href=&quot;https://en.wikipedia.org/wiki/XHTML&quot;&gt;XHTML&lt;/a&gt;, HTML 5 brought us new hope. Many new elements were added based on existing naming conventions to pave the cow paths. The new spec has challenged browsers for years ahead, from supporting the new parsing algorithm to default styles and accessibility mappings.&lt;/p&gt;
&lt;p&gt;For the Web standards believers of the old spec, the new one was just a promised land:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/blog/aria-accessibility-html-landmark-roles/&quot;&gt;Landmarks&lt;/a&gt; to mark logical parts like headers, footers, asides, navigations, sections, and articles.&lt;/li&gt;
&lt;li&gt;Variety of new form elements other than the text ones: &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/date&quot;&gt;dates&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/email&quot;&gt;emails&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/number&quot;&gt;numbers&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/range&quot;&gt;ranges&lt;/a&gt;, and &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/color&quot;&gt;colors&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Media and interactive elements for &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/video&quot;&gt;video&lt;/a&gt;, &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/audio&quot;&gt;audio&lt;/a&gt;, and &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/picture&quot;&gt;graphics&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There was even a logo for semantics in the &lt;a href=&quot;https://www.w3.org/html/logo/&quot;&gt;HTML 5’s design&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pepelsbey.dev/articles/road-to-htmhell/images/semantics.svg&quot; alt=&quot;Semantics logo with three horizontal angled lines pointing up.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Apart from extending the list of functional building blocks, the spec added several semantic elements that didn’t even come with any styling, just meaning. But not only that! Some old, purely visual elements were lucky enough not to be deprecated but redefined. For example, &lt;code&gt;&amp;lt;b&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;i&amp;gt;&lt;/code&gt; became cool again, though no one could explain the use cases, apart from rather vague taxonomy and emphasis ones and… naming ships. You think I’m kidding? &lt;a href=&quot;https://html.spec.whatwg.org/multipage/text-level-semantics.html#the-i-element&quot;&gt;Check the spec!&lt;/a&gt;&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;Boaty McBoatface&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;i&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Don’t get me wrong, I think HTML 5 significantly advanced the Web, but it has also detached us from reality even further. Especially the idea of an outline algorithm and multiple nested &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; elements that would change the level based on nesting. It was never implemented by any browser but existed in the spec for a long, long time &lt;a href=&quot;https://github.com/whatwg/html/pull/7829&quot;&gt;until finally removed in 2022&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;section&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;Please&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;section&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;Don’t use&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;section&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;			&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;This code!&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;section&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;section&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;section&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;⚠️ Please don’t use the code above. It’s wrong and harmful.&lt;/p&gt;
&lt;p&gt;Personally, I’ve wasted too many hours arguing about the difference between &lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;section&amp;gt;&lt;/code&gt; for purely theoretical reasons instead of focusing on good user experience.&lt;/p&gt;
&lt;h2&gt;Drunk on semantics&lt;/h2&gt;
&lt;p&gt;Although the spec would provide examples, it primarily focused on marking up content, not UI. Even examples themselves were often purely theoretical with a kind of usage that would be semantically correct, not always practically useful. There’s &lt;a href=&quot;https://www.w3.org/wiki/HTML/W3C-WHATWG-Differences&quot;&gt;another whole story&lt;/a&gt; about the difference between the W3C and WHATWG spec versions, but the W3C’s examples were usually better.&lt;/p&gt;
&lt;p&gt;I’ve seen a lot of weird stuff and did it myself, too. People would often look at the HTML spec as a dictionary, looking up a word in the list of elements for an idea they had in mind. Try to read the following examples through the eyes of a beginner, giving a shallow look at the spec. They totally make sense!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;menu&amp;gt;&lt;/code&gt; for wrapping the navigation menus.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt; for the content of an article.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;input type=&amp;quot;number&amp;quot;&amp;gt;&lt;/code&gt; for a phone number.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; for everything that looks like a button.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I haven’t seen the &lt;code&gt;&amp;lt;slot&amp;gt;&lt;/code&gt; element used on a casino website to mark up a slot machine, but maybe only because I’m not into gambling. But the rest of the examples are real.&lt;/p&gt;
&lt;p&gt;At the same time, a lot of people would read the spec carefully and use &lt;code&gt;&amp;lt;footer&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;header&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt;, and other semantic elements properly. But the reason for that won’t be any different: they would also aim for semantically correct markup only because the spec says so. And if it does, the smartest of us would think it should be good for users, search engines, etc. Right?&lt;/p&gt;
&lt;p&gt;It turned out that the spec could be wrong, and &lt;strong&gt;semantically correct markup wouldn’t guarantee good practical results&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;I don’t blame people who gave up on following the spec altogether and became cynical enough to use &lt;code&gt;&amp;lt;i&amp;gt;&lt;/code&gt; for icons instead of naming damn ships. Fortunately, I didn’t go this way. I found another reason to keep caring about markup: user experience and accessibility.&lt;/p&gt;
&lt;h2&gt;Good intentions&lt;/h2&gt;
&lt;p&gt;Unlike many other languages, HTML is a user-facing one. It means that &lt;strong&gt;our decisions directly affect users&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Fortunately, it doesn’t matter how we format our markup, but our selection of elements matters a lot. So when I hear “this markup is semantic,” it often means that it’s correct according to the spec but not exactly good for actual users. Even though both can be true at the same time, the focus is in the wrong place.&lt;/p&gt;
&lt;p&gt;It seems to me that we decided to trust the spec’s recommendations at some point without checking whether they were true. I firmly believe that the spec authors’ intentions are always good, and I know many smart people working on the HTML spec. But when it comes to implementation in browsers or screen readers, these intentions don’t always survive the reality.&lt;/p&gt;
&lt;p&gt;There are usually three main obstacles:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Product priorities: you probably know that already, but accessibility isn’t always a number one priority for various reasons, including complexity and the lack of people who know the area.&lt;/li&gt;
&lt;li&gt;Different points of view: for the same reason, automated testing won’t save you from accessibility issues, different user agents might have other points of view on certain platform features.&lt;/li&gt;
&lt;li&gt;Actual user experience: browsers call themselves “user agents” for a reason. When a specific platform feature or how developers use it hurts the users, browsers tend to intervene.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For example, the following list won’t be exposed as a list to VoiceOver in Safari only because you decided to disable default bullets and implement custom ones via CSS pseudo-elements.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;ul&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; style&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;list-style: none&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;li&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;Item&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;li&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;li&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;Item&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;li&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;ul&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can force the usual behavior by adding &lt;code&gt;role=&amp;quot;list&amp;quot;&lt;/code&gt; to every list you style, but how convenient is that? Not at all for you as a developer. But Safari has probably had some reasons, most likely to improve their users’ experience by ignoring all semantically correct lists we started using so much outside of content.&lt;/p&gt;
&lt;p&gt;As for the screen readers, Steve Faulkner’s “&lt;a href=&quot;https://www.tpgi.com/screen-readers-support-for-text-level-html-semantics/&quot;&gt;Screen Readers support for text level HTML semantics&lt;/a&gt;” article might open your eyes to the actual value of those tags we’re so passionately arguing about.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;No browsers expose &lt;code&gt;&amp;lt;strong&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;em&amp;gt;&lt;/code&gt; element role semantics in the accessibility tree.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Again, you can force some semantics via ARIA roles, but should you? That’s an open question. The answer depends on the value you’re trying to bring your users.&lt;/p&gt;
&lt;p&gt;Does it mean we should immediately stop using semantic elements if they don’t bear any value for the users? I don’t think so. But I stopped using a &lt;em&gt;semantics argument&lt;/em&gt; when talking about good markup. Just like tabs and spaces, semicolons, or quotes, semantics is sometimes a stylistic preference.&lt;/p&gt;
&lt;p&gt;There’s also a future-proofing argument that suggests using semantic markup with the hope that someday, browsers will start supporting all those elements they choose to ignore now. I wouldn’t rely on it too much and prefer to focus on what’s important right now.&lt;/p&gt;
&lt;p&gt;I used to be among those people who’d judge the quality of a website based on the number of divs it’s built of. We’d say, “Nah, too many divs, it’s not semantic.” Now I know that &lt;strong&gt;what’s inside those divs matters the most&lt;/strong&gt;. Enough landmarks, headings, links, and buttons would make it good, even if the divs/semantic elements ratio is 1000 to 10. We are &lt;em&gt;divelopers,&lt;/em&gt; &lt;a href=&quot;https://twitter.com/chriscoyier/status/1050456501414838272&quot;&gt;as Chris Coyier once said&lt;/a&gt;. Don’t be ashamed of this. Wear this name with pride.&lt;/p&gt;
&lt;h2&gt;Training wheels&lt;/h2&gt;
&lt;p&gt;Following the spec’s recommendations with semantic markup is still a good start, especially when you treat it as not just the list of available elements. I mostly agree with this idea often expressed by accessibility experts:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you write semantic markup, it will be mostly accessible.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;But to me, it sounds like a simple answer to a complex question. The HTML spec might be a good set of training wheels, but you’ll have to take them off at some point. Not everything can be solved by semantic markup. For example, you’ll need to learn ARIA to create any modern interactive UI. There’s just not enough semantic elements for everything!&lt;/p&gt;
&lt;p&gt;Many simple answers are waiting for you in the spec or articles praising semantics as the only thing you need. Even more compromises are made in modern frameworks in the name of better developer experience. And they aren’t all wrong! But if you keep your focus on the user experience, on the actual quality of the user interface, you’ll be able to make the right decisions.&lt;/p&gt;
&lt;p&gt;And you know what? It doesn’t matter if you agree with me on the value of semantics. I’m sure you’ll be fine. After all, you’ve just read a big rant on HTML.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;Initially published in the &lt;a href=&quot;https://www.htmhell.dev/adventcalendar/2023/18/&quot;&gt;HTMHell advent calendar&lt;/a&gt; in December 2023. Thank you to &lt;a href=&quot;https://www.matuzo.at/&quot;&gt;Manuel Matuzovic&lt;/a&gt; and &lt;a href=&quot;https://yatil.net/&quot;&gt;Eric Eggert&lt;/a&gt; for proofreading and feedback.&lt;/em&gt;&lt;/p&gt;
			</content></entry><entry><title>Uppercase copy and paste. The problem and a question&amp;shy;able trick</title><link href="https://pepelsbey.dev/articles/uppercase-copy-paste/"/><updated>2023-04-03T00:00:00Z</updated><id>https://pepelsbey.dev/articles/uppercase-copy-paste/</id><content type="html">&lt;p&gt;The other day &lt;a href=&quot;https://blog.tomayac.com/&quot;&gt;Thomas Steiner&lt;/a&gt; kindly decided to share my “&lt;a href=&quot;https://pepelsbey.dev/articles/jumping-html-tags/&quot;&gt;Jumping HTML tags&lt;/a&gt;” article and got frustrated because after selecting and copying the title of the article he got this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;JUMPING HTML TAGS. ANOTHER REASON TO VALIDATE YOUR MARKUP&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It wasn’t some rich text editor, just regular text input. I suppose Thomas didn’t want to shout at his readers and had to normalize the case before &lt;a href=&quot;https://toot.cafe/@tomayac/110108026079630471&quot;&gt;sharing it&lt;/a&gt;. Thank you, Thomas! 😊&lt;/p&gt;
&lt;p&gt;But why would I type my titles in uppercase? First of all, this is how &lt;a href=&quot;https://www.facebook.com/mark.jpeg.71&quot;&gt;Mark Shakhov&lt;/a&gt; designed it, and I like it a lot. Second, I don’t actually type them like that: you can check &lt;a href=&quot;https://pepelsbey.dev/articles/&quot;&gt;the list of all articles&lt;/a&gt; where the case is normal. I use CSS to style it like this only on the article page:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;.lead__title&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	margin&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	line-height&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;1.1&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	text-wrap&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;balance&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	text-transform&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;uppercase&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	font-weight&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;normal&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	font-family&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FFAB70;--shiki-light:#E36209&quot;&gt;--font-family-heading&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At first, it didn’t make any sense: I often copy titles of my newly published articles to share them on different platforms: &lt;a href=&quot;https://mastodon.social/@pepelsbey&quot;&gt;Mastodon&lt;/a&gt;, &lt;a href=&quot;https://twitter.com/pepelsbey_dev&quot;&gt;Twitter&lt;/a&gt;, &lt;a href=&quot;https://t.me/pepelsbey_dev&quot;&gt;Telegram&lt;/a&gt;. So I tried to copy the title in Firefox, my browser of choice, and I got a pretty reasonable result:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Jumping HTML tags. Another reason to validate your markup&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Then I did the same in Chrome and Safari and got the uppercase. There we go again 🙄&lt;/p&gt;
&lt;div class=&quot;update&quot;&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; Chrome changed the behavior to match Firefox’s in version 127, released on July 23rd, 2024, although it wasn’t mentioned in the &lt;a href=&quot;https://developer.chrome.com/release-notes/127&quot;&gt;release notes&lt;/a&gt;. But enough spoilers, keep reading.&lt;/p&gt;
&lt;/div&gt;
&lt;h2&gt;The problem&lt;/h2&gt;
&lt;p&gt;As I mentioned in the article that caused it, Web standards are the main thing that holds the whole Web platform together. In our case, it’s the &lt;a href=&quot;https://www.w3.org/TR/css-text-3/#propdef-text-transform&quot;&gt;CSS Text Module&lt;/a&gt; spec, which says, plain and simple, about the &lt;code&gt;text-transform&lt;/code&gt; property:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This property transforms text for styling purposes. It has no effect on the underlying content, and must not affect the content of a plain text copy &amp;amp; paste operation.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It means that Chrome and Safari are wrong. Whether you agree with this behavior or not, it’s against the spec. Unfortunately, I couldn’t find a relevant test in the &lt;a href=&quot;https://wpt.fyi/results/css/css-text/text-transform?label=experimental&amp;amp;label=master&amp;amp;aligned&quot;&gt;WPT suite&lt;/a&gt; for the &lt;code&gt;text-transform&lt;/code&gt; property. But it’s not just Firefox that goes against the crowd. Two other browsers with independent engines used to do the same: Internet Explorer and Opera 12 on Presto.&lt;/p&gt;
&lt;p&gt;I’m late to the party here, it’s been discussed for years. There are browser bugs in &lt;a href=&quot;https://bugs.chromium.org/p/chromium/issues/detail?id=325231&quot;&gt;Chrome&lt;/a&gt; and &lt;a href=&quot;https://bugs.webkit.org/show_bug.cgi?id=43202&quot;&gt;Safari&lt;/a&gt; that you can subscribe to or even better, vote for and post your use cases. And if you enjoy lengthy CSSWG discussions, grab yourself a drink and read &lt;a href=&quot;https://github.com/w3c/csswg-drafts/issues/3775&quot;&gt;this one&lt;/a&gt; started by &lt;a href=&quot;https://bkardell.com/&quot;&gt;Brian Kardell&lt;/a&gt;. And the classic “&lt;a href=&quot;https://adrianroselli.com/2012/06/copying-content-styled-with-text.html&quot;&gt;Copying content styled with text-transform&lt;/a&gt;” article by Adrian Roselly published in 2012, giving it the accessibility perspective.&lt;/p&gt;
&lt;p&gt;I called the next part “The solution” at first, but honestly, it’s a questionable trick 🙃&lt;/p&gt;
&lt;h2&gt;A questionable trick&lt;/h2&gt;
&lt;p&gt;Apart from how it’s described in the spec and my opinion on what browser is correct (the one that’s not causing frustration), there’s a user perspective to consider. I don’t want readers of my blog to normalize the case or dig into the source code whenever they want to share my articles. I’d like the behavior described in the spec to be the default one for Chrome and Safari users. Damn, I want to be able to select the title of my article on my iPhone and get the actual title, not the shouty version of it.&lt;/p&gt;
&lt;p&gt;That’s why I quickly put together a small script that hijacks the &lt;code&gt;copy&lt;/code&gt; event and puts the source text in the clipboard. The event is only fired when the whole title or some part of it is selected. It works just fine via shortcuts, context menus, or select tooltips on touch devices.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;document&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	.&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;querySelector&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;.lead__title&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	.&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;addEventListener&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;copy&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;, (&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FFAB70;--shiki-light:#E36209&quot;&gt;event&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		event.clipboardData.&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;setData&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;			&#39;text/plain&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;			event.target.textContent&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		event.&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;preventDefault&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I was pleasantly surprised to see how easy it was to implement. But of course, it’s just a questionable trick with downsides. It’ll copy the whole title even if you select a single word. If you extend the selection beyond the title, it’ll copy just the title. I’m pretty sure there’s more, but fortunately, “Select All” or a significant selection extension stops the script from hijacking the event.&lt;/p&gt;
&lt;p&gt;Would I recommend using this script? Only if you really need this and there’s no other way of working around the issue. Meanwhile, I hope this wave of interest will lead browsers and CSSWG to a better solution. It’s a tricky problem, and there’s the truth behind both arguments. We might need a new property to control this behavior, but I’d start by aligning with the spec.&lt;/p&gt;
			</content></entry><entry><title>Jumping HTML tags. Another reason to validate your markup</title><link href="https://pepelsbey.dev/articles/jumping-html-tags/"/><updated>2023-03-29T00:00:00Z</updated><id>https://pepelsbey.dev/articles/jumping-html-tags/</id><content type="html">&lt;style&gt;
	.frameworks {
		display: flex;
		column-gap: 0.8rem;
	}

	.frameworks__item {
		display: inline-flex;
		align-items: center;
		gap: 0.125rem;
	}

	.frameworks__item::before {
		content: &#39;&#39;;
		width: 2em;
		height: 2em;
	}

	.frameworks__item--react::before {
		background-image: url(&#39;images/frameworks.svg#react&#39;);
	}

	.frameworks__item--angular::before {
		background-image: url(&#39;images/frameworks.svg#angular&#39;);
	}

	.frameworks__item--svelte::before {
		background-image: url(&#39;images/frameworks.svg#svelte&#39;);
	}

	.frameworks__item--preact::before {
		background-image: url(&#39;images/frameworks.svg#preact&#39;);
	}

	.frameworks__item--vue::before {
		background-image: url(&#39;images/frameworks.svg#vue&#39;);
	}

	.frameworks__item--lit::before {
		background-image: url(&#39;images/frameworks.svg#lit&#39;);
	}
&lt;/style&gt;
&lt;p&gt;If you’re building for the Web, you’re most likely writing HTML. It could be JSX, Markdown, or even Dart in your code editor, but eventually, it gets compiled to some sort of markup. And the further away from the actual tags you get, the less idea you have of what gets there. For most developers, it’s just an artifact, like a binary file.&lt;/p&gt;
&lt;p&gt;And this is fine, I guess 🤔 We use abstraction layers for solving complex problems. At least, that’s &lt;a href=&quot;https://reactjs.org/docs/introducing-jsx.html#why-jsx&quot;&gt;what they say&lt;/a&gt;. Don’t get me wrong, it often gets ugly: ask &lt;a href=&quot;https://www.htmhell.dev/&quot;&gt;HTMHell&lt;/a&gt; for examples. Fortunately, in most cases, browsers are smart enough to handle our poor markup.&lt;/p&gt;
&lt;p&gt;But sometimes browsers take our mistakes personally, and tags start jumping around 😳&lt;/p&gt;
&lt;h2&gt;Basic nesting&lt;/h2&gt;
&lt;p&gt;I’m sure most of the developers haven’t read the &lt;a href=&quot;https://html.spec.whatwg.org/multipage/&quot;&gt;HTML spec&lt;/a&gt;. They might have stumbled upon it, but I get it: it’s not something you read for fun. But somehow, they more or less know how HTML works and some basic rules, including tags nesting. It’s like our native language: we’ve learned it by listening to our parents and perfected it by speaking.&lt;/p&gt;
&lt;p&gt;For example, we all have learned that &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt; can only contain &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt;. Because, you know, it’s an &lt;em&gt;unordered list&lt;/em&gt; and a &lt;em&gt;list item&lt;/em&gt;. And just like in a natural language, we can move things around, and it will still make sense. We can use paragraphs instead of list items, and it will still be fine: no bullets and some extra margins, but nothing too scary.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#6A737D;--shiki-light:#6A737D&quot;&gt;&amp;#x3C;!-- Source &amp;#x26; DOM --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;ul&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;ul&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this case, the source markup will be represented exactly the same in the DOM tree. But it feels wrong, right? It does, but I wouldn’t rely on this feeling too much. &lt;a href=&quot;https://youtu.be/P1MJU6l_1WM&quot;&gt;HTML is a programming language&lt;/a&gt;, after all. It’s wrong because the HTML spec says so:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://html.spec.whatwg.org/multipage/grouping-content.html#the-ul-element&quot;&gt;The &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt; element&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Categories: Flow content&lt;/li&gt;
&lt;li&gt;Content model: Zero or more &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://html.spec.whatwg.org/multipage/grouping-content.html#the-p-element&quot;&gt;The &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; element:&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;Categories: Flow content&lt;/li&gt;
&lt;li&gt;Content model: Phrasing content&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt;’s content model allows zero or more &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt; elements and nothing else, apart from some scripting elements. But browsers don’t care about it, so why should we? There are many good reasons to align your markup with the spec, but let me give you the one that’s rarely mentioned.&lt;/p&gt;
&lt;p&gt;Let’s turn the whole thing upside down and put the &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt; inside the &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#6A737D;--shiki-light:#6A737D&quot;&gt;&amp;#x3C;!-- Source --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;ul&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;ul&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt;’s content model allows only phrasing content, and &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt; is flow content. But who cares? Browsers are still going to render a list inside a para… What the hell? 😬&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#6A737D;--shiki-light:#6A737D&quot;&gt;&amp;#x3C;!-- DOM --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;ul&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;ul&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;What the hell?&lt;/h2&gt;
&lt;p&gt;Yes, our &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt; just tore the &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; apart by being a &lt;em&gt;wrong&lt;/em&gt; element. And this is a common behavior among modern browsers, all according to the spec. I couldn’t find a specific place in the spec that says “tear the &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; apart, but keep &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt; intact” (the &lt;a href=&quot;https://html.spec.whatwg.org/multipage/parsing.html#parsing&quot;&gt;parsing section&lt;/a&gt; is pretty huge), but it &lt;em&gt;should&lt;/em&gt; be in there one way or another since browsers agree on this behavior.&lt;/p&gt;
&lt;p&gt;My favorite section is “&lt;a href=&quot;https://html.spec.whatwg.org/multipage/parsing.html#unexpected-markup-in-tables&quot;&gt;Unexpected markup in tables&lt;/a&gt;”, which starts with:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Error handling in tables is, for historical reasons, especially strange.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And then tries to explain how browsers should handle the following markup:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;table&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;b&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;tr&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;td&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;aaa&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;td&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;tr&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;bbb&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;table&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;ccc&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Look at the result, you’ll be fascinated. Now, this is something I’d read for fun! 😁&lt;/p&gt;
&lt;h3&gt;Jumping examples&lt;/h3&gt;
&lt;p&gt;But it’s not just paragraphs that hate you. The tables are pretty picky, too. They don’t like to host random elements inside. A &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; inside of the &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt; will jump out of it, but the &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt; will hold it together and won’t split, unlike the &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#6A737D;--shiki-light:#6A737D&quot;&gt;&amp;#x3C;!-- Source --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;table&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;Jump!&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;table&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#6A737D;--shiki-light:#6A737D&quot;&gt;&amp;#x3C;!-- DOM --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;Jump!&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;table&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;table&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But if you decide to nest table parts outside of the &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt;, they’ll just disappear 🫥 No tags, no problems.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#6A737D;--shiki-light:#6A737D&quot;&gt;&amp;#x3C;!-- Source --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;td&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;I’m not here!&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;td&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#6A737D;--shiki-light:#6A737D&quot;&gt;&amp;#x3C;!-- DOM --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;I’m not here!&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nesting interactive elements into one another is a bad idea on its own, but sometimes it comes with special effects. If you nest buttons or links, the inner one will jump out of it.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#6A737D;--shiki-light:#6A737D&quot;&gt;&amp;#x3C;!-- Source --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	Outer&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;Inner&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#6A737D;--shiki-light:#6A737D&quot;&gt;&amp;#x3C;!-- DOM --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;Outer&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;Inner&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;button&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But if you nest a button inside a link or vice versa, nothing will happen. They don’t like to nest only the ones of their kind (some family issues, perhaps). But in this case, it obviously looks broken, right? We almost expect it to fail by common sense. Let’s look at something a bit more practical.&lt;/p&gt;
&lt;h3&gt;Product card&lt;/h3&gt;
&lt;p&gt;We all know this “product card” pattern: title, description, some picture, and the whole thing is a link. According to the spec, having this card wrapped in a link is fine. But once there’s a link somewhere in the description…&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#6A737D;--shiki-light:#6A737D&quot;&gt;&amp;#x3C;!-- Source --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;article&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;h2&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;Jumping HTML tags&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;h2&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;			Another reason to&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;			&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;validate&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;			your markup.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;article&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It’s even hard to describe what happens here. Just look at the DOM 😳&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#6A737D;--shiki-light:#6A737D&quot;&gt;&amp;#x3C;!-- DOM --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;article&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;h2&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;Jumping HTML tags&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;h2&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;Another reason to &amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;validate&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		your markup.&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;article&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Another problem with this approach is that even if you avoid the nesting links, the whapper link’s content is not a good accessible description. On this website, I used the trick with an absolutely positioned pseudo-element. You can read more about it in Heydon Pickering’s “&lt;a href=&quot;https://inclusive-components.design/cards/#thepseudocontenttrick&quot;&gt;Cards&lt;/a&gt;” article.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Everything that we’ve just discussed was the plain markup. But I wonder what a DOM generated with JavaScript would look like. This would be useful to understand for all JS frameworks out there. Remember all those abstract layers? Yeah. But let’s start with the basics.&lt;/p&gt;
&lt;h2&gt;DOM via JS&lt;/h2&gt;
&lt;p&gt;There are two ways of generating DOM with JavaScript: setting the whole thing to &lt;code&gt;innerHTML&lt;/code&gt; (or similar) or one element at a time via &lt;code&gt;createElement()&lt;/code&gt; from DOM API. Let’s start with the first one:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;document.body.innerHTML &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt; `&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;	&amp;#x3C;p&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;		&amp;#x3C;ul&gt;&amp;#x3C;/ul&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;	&amp;#x3C;/p&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;`&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we’re asking the browser to make sense of this string and build a DOM tree based on that. You might even call it declarative. In this case, we’ll get the same result as with the plain markup before: the &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; is torn apart again 🫠&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#6A737D;--shiki-light:#6A737D&quot;&gt;&amp;#x3C;!-- DOM --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;ul&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;ul&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But if we specifically ask the browser to create elements, combine them in a certain way, and then append them to the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; ul&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; document.&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;createElement&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;ul&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; p&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; document.&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;createElement&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;p&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;p.&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;appendChild&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(ul);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;document.body.&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;appendChild&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(p);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we’ll get exactly what we’ve asked for:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#6A737D;--shiki-light:#6A737D&quot;&gt;&amp;#x3C;!-- DOM --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;ul&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;ul&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It means that by using the DOM API, we can force the browser to render any nonsense markup we want. Let’s see what JS frameworks chose to do with this.&lt;/p&gt;
&lt;h2&gt;DOM via frameworks&lt;/h2&gt;
&lt;p&gt;As I mentioned initially, we often use abstraction layers to generate markup. Somewhere deep down the framework guts, the actual markup is produced. After some brief testing, I’ve found that all major frameworks could be split into three groups based on how they handle incorrect nesting:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Care about mistakes and report errors.&lt;/li&gt;
&lt;li&gt;Just generate whatever you tell them to.&lt;/li&gt;
&lt;li&gt;Output the same DOM as browsers would.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I tested the &lt;code&gt;p &amp;gt; ul&lt;/code&gt; example in a few major frameworks: React, Angular, Svelte, Vue, Preact, and Lit. It should give us a good idea of how things work across the board.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#6A737D;--shiki-light:#6A737D&quot;&gt;&amp;#x3C;!-- Source --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;ul&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;ul&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Care a lot&lt;/h3&gt;
&lt;p class=&quot;frameworks&quot;&gt;
	&lt;span class=&quot;frameworks__item frameworks__item--react&quot;&gt;React&lt;/span&gt;
	&lt;span class=&quot;frameworks__item frameworks__item--angular&quot;&gt;Angular&lt;/span&gt;
	&lt;span class=&quot;frameworks__item frameworks__item--svelte&quot;&gt;Svelte&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;First of all, why would they even care? One of the reasons is consistency between server-rendered and client-rendered markup. Yes, the framework will generate the same markup in both cases, but the server one will be transformed into DOM and “fixed” by the browser. The client one will be inserted into the DOM as is.&lt;/p&gt;
&lt;p&gt;To ensure that the browser won’t mess with the markup, these frameworks report incorrect nesting when they see it. Well, some of it, more on that later. The error messages convey more or less the same idea: the nesting is wrong.&lt;/p&gt;
&lt;p&gt;In React’s case, it’s clear and to the point:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Warning: validateDOMNesting(…): &amp;lt;ul&amp;gt; cannot appear as a descendant of &amp;lt;p&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;They took care of this &lt;a href=&quot;https://github.com/facebook/react/commit/f9abf493b4685869e9feed5738d8271b0dc4e944#diff-93f9ab60008590ba55d1b674c1335520b47d7748488fe839e425498b7d533af5R24-R138&quot;&gt;back in 2015&lt;/a&gt; by categorizing all elements into spec-based groups (flow, phrasing, etc.) and mapping them with nesting rules. Today it looks &lt;a href=&quot;https://github.com/facebook/react/blob/main/packages/react-dom-bindings/src/client/validateDOMNesting.js&quot;&gt;a bit different&lt;/a&gt;, but the idea is more or less the same: they don’t care if the markup is “valid,” they only care if it’s going to be “fixed” by the browser.&lt;/p&gt;
&lt;p&gt;Angular suggests that some tags weren’t closed properly, which is not the case, really. And the message sounds like it has no idea what’s going on:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Template parse errors: Unexpected closing tag “p”. It may happen when the tag has already been closed by another tag. For more info see &lt;a href=&quot;https://www.w3.org/TR/html5/syntax.html#closing-elements-that-have-implied-end-tags&quot;&gt;https://www.w3.org/TR/html5/syntax.html&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;They even give you the link to an outdated spec that, fortunately, redirects to the Living Standard. But this “implied end tags” section won’t ever help you to understand the issue 😔&lt;/p&gt;
&lt;p&gt;Svelte doesn’t help much either, but at least it sounds a bit more confident:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;lt;/p&amp;gt; attempted to close &amp;lt;p&amp;gt; that was already automatically closed by &amp;lt;ul&amp;gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There’s a special case here: Vue, when paired with some SSR engine like &lt;a href=&quot;https://nuxtjs.org/&quot;&gt;Nuxt&lt;/a&gt;, might also report “&lt;a href=&quot;https://vuejs.org/guide/scaling-up/ssr.html#hydration-mismatch&quot;&gt;hydration mismatch&lt;/a&gt;” errors. Not the best error name, but at least it tries to warn you. But that’s not the case with the plain Vue.&lt;/p&gt;
&lt;h3&gt;Do what you tell them&lt;/h3&gt;
&lt;p class=&quot;frameworks&quot;&gt;
	&lt;span class=&quot;frameworks__item frameworks__item--preact&quot;&gt;Preact&lt;/span&gt;
	&lt;span class=&quot;frameworks__item frameworks__item--vue&quot;&gt;Vue&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;Well, they do. It probably simplifies the implementation since you don’t have to carry around all the rules from the spec and keep them up to date. You just need to &lt;code&gt;createElement&lt;/code&gt; and append it somewhere. I guess they’re fine with the lack of consistency between server and client, but I’m not sure how big of a deal it is.&lt;/p&gt;
&lt;h3&gt;Like browsers&lt;/h3&gt;
&lt;p class=&quot;frameworks&quot;&gt;
	&lt;span class=&quot;frameworks__item frameworks__item--lit&quot;&gt;Lit&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;No errors, just the “fixed” DOM with the &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; torn apart. It most likely uses &lt;code&gt;innerHTML&lt;/code&gt; under the hood at some point. The responsibility for the output is shifted to developers, but it’s easier to handle since it’s consistent with the browser’s behavior.&lt;/p&gt;
&lt;h3&gt;Not quite&lt;/h3&gt;
&lt;p&gt;Among the “care a lot” frameworks, React’s clear error messages and spec compatibility stand out. Both Angular and Svelte don’t consider the nested buttons example a mistake. But the problem is that none of them managed to handle the product card wrapped in a link example. Even React didn’t catch the wrong nesting and rendered the DOM as the source 😬&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;So, the winner is Lit, that’s not even trying to construct something different from what browsers would do. But I’m grateful to React for trying to be spec-compliant. With all that, what should we do?&lt;/p&gt;
&lt;h2&gt;Validate it&lt;/h2&gt;
&lt;p&gt;I know, after everything we’ve been through here, you might be thinking that it’s a mess. With different frameworks doing their own thing on top of that. But this kind of “behind-the-scenes controlled complexity” mess is holding everything together. How all browsers recover from our mistakes exactly the same way is fascinating.&lt;/p&gt;
&lt;p&gt;Just like Alex Russell said &lt;a href=&quot;https://f-word.dev/episodes/14/&quot;&gt;in the recent The F-Word episode&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;You can take some HTML, write it down on a back of a napkin, put it in your pocket, put it in the wash, grab it out of the dryer, uncrumple a little bit, type it back in with a bunch of typos and it will probably render something. And probably not something super different from what you meant.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is one of the best foundations for the Web Platform we can dream of. But I’d still try to avoid wrong nesting in the first place. Remember the product card? There were four links in the resulting DOM instead of two. And one of them wrapping the header. Imagine how much harm it could do to the functionality and accessibility of the page.&lt;/p&gt;
&lt;p&gt;Not just that! Apparently, misplaced HTML elements could cause performance issues 😭 There’s a good example in Harry Roberts’ “&lt;a href=&quot;https://youtube.com/watch?v=vgZ2B0rY4fs&amp;amp;t=791&quot;&gt;Get Your Head Straight&lt;/a&gt;” talk: one simple stray &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; could mess up &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; parsing and degrade page loading.&lt;/p&gt;
&lt;p&gt;Fortunately, there are a few tools that can help you avoid this kind of mistakes.&lt;/p&gt;
&lt;h3&gt;W3C HTML validator&lt;/h3&gt;
&lt;p&gt;This is the closest thing to the spec you can get. Most of you probably know it as an online service at &lt;a href=&quot;https://validator.w3.org/&quot;&gt;validator.w3.org&lt;/a&gt;, where you can input an address, upload a file, or paste the markup. I use this service to check something quickly. But it’s 2023, and we need a tool that constantly checks markup for us. You know, CI/CD and all that 🤓&lt;/p&gt;
&lt;p&gt;The tool behind it is called &lt;a href=&quot;https://github.com/validator/validator&quot;&gt;Nu Html Checker&lt;/a&gt; or v.Nu, it’s open-source and written in Java. I don’t have Java installed on my system, so I could’ve used the official &lt;a href=&quot;https://validator.github.io/validator/#docker-image&quot;&gt;Docker image&lt;/a&gt; to run it locally. But instead, for my blog, I use a convenient and official npm package &lt;a href=&quot;https://www.npmjs.com/package/vnu-jar&quot;&gt;vnu-jar&lt;/a&gt; running all checks in GitHub Actions. Here’s the npm script I run for that:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;java&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	-jar&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt; node_modules/vnu-jar/build/dist/vnu.jar&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	--filterfile&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt; .vnurc&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	dist/**/*.html&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s unfold this command:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;-jar&lt;/code&gt; option specifies the path to the installed tool.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;--filterfile&lt;/code&gt; passes the list of errors I’d like to ignore.&lt;/li&gt;
&lt;li&gt;Then follows the list of files to check.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;There are many more options &lt;a href=&quot;https://validator.github.io/validator/&quot;&gt;in the documentation&lt;/a&gt;, but I’d like to focus on &lt;code&gt;--filterfile&lt;/code&gt; a bit more. You know, the validator is not always correct: sometimes, you use features that aren’t in the spec yet, or you just know what you’re doing and want to ignore the warning.&lt;/p&gt;
&lt;p&gt;The file is just a list of messages to ignore, one per line. But they’re also regular expressions, so you have some flexibility here. For example, here’s the list of messages I decided to ignore:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Attribute “media” not allowed on element “meta” at this point.&lt;/li&gt;
&lt;li&gt;Attribute “fetchpriority” not allowed on element “img” at this point.&lt;/li&gt;
&lt;li&gt;Possible misuse of “aria-label”.*&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can copy those messages from the tool’s output. But be careful, sometimes the exact messages don’t match, which is a &lt;a href=&quot;https://github.com/validator/validator/issues/1070&quot;&gt;known issue&lt;/a&gt;. In this case, you might want to use some wildcards to match it. For example, I used the inline &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; element in this article to add framework icons, and the validator considers this a mistake. With all due respect, your honor, I disagree 🧐 But I couldn’t match and filter out the following message:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Element “style” not allowed as child of element “body” in this context.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So I used this one instead, and it worked. Note the &lt;code&gt;.*&lt;/code&gt; at the end:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Element “style” not allowed as child of element.*&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;As for the GitHub Actions workflow that runs this script, there’s nothing special in there. But if you’re interested, you can &lt;a href=&quot;https://github.com/pepelsbey/pepelsbey.dev/blob/main/.github/workflows/html.yml&quot;&gt;check it out&lt;/a&gt;. I’d also recommend checking out the &lt;a href=&quot;https://github.com/twbs/bootstrap/blob/main/build/vnu-jar.mjs&quot;&gt;Bootstrap’s script&lt;/a&gt; that runs the validation. It’s a bit more sophisticated and doesn’t break the tests if Java is missing.&lt;/p&gt;
&lt;h3&gt;HTML-validate&lt;/h3&gt;
&lt;p&gt;It’s an independent tool trying to be a bit more flexible than the validator. It can check not only full documents but also fragments of HTML. Because of that, it might be helpful for testing components. Unlike the W3C’s validator, it’s not trying to be 100% spec-compliant and might not catch some nuances. However, it managed to spot the product card’s mistake.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;lt;a&amp;gt; element is not permitted as a descendant of &amp;lt;a&amp;gt; (element-permitted-content)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I didn’t have a chance to try it myself, but it seems like a good option if the official validator doesn’t fit your CI/CD workflow or you need to validate the markup of a component. You can read more about it in the &lt;a href=&quot;https://html-validate.org/&quot;&gt;documentation&lt;/a&gt;, check out the &lt;a href=&quot;https://gitlab.com/html-validate/html-validate&quot;&gt;source code&lt;/a&gt;, and even &lt;a href=&quot;https://online.html-validate.org/&quot;&gt;try it online&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;HTML Linters&lt;/h3&gt;
&lt;p&gt;Linters usually care about the code you’re writing, not the output. They might help you with your component’s markup, but how components work together is out of their scope. But every bit helps, right?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;a href=&quot;https://github.com/MananTank/validate-html-nesting&quot;&gt;validate-html-nesting&lt;/a&gt; library implements the same principle as React: to check nesting based on categories and content models of particular elements. There are &lt;a href=&quot;https://github.com/MananTank/eslint-plugin-validate-jsx-nesting&quot;&gt;ESLint&lt;/a&gt; and &lt;a href=&quot;https://github.com/MananTank/validate-jsx-nesting&quot;&gt;Babel&lt;/a&gt; plugins for JSX based on this tool. The downsides are the same: you won’t catch some more complex cases, like product card.&lt;/li&gt;
&lt;li&gt;The &lt;a href=&quot;https://htmlhint.com/&quot;&gt;HTMLHint&lt;/a&gt; doesn’t care about your nesting, but it might help you implement a specific code style and catch some common mistakes. The &lt;a href=&quot;https://htmlhint.com/docs/user-guide/list-rules&quot;&gt;list of rules&lt;/a&gt; is not very long, but all of them are useful.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;No, seriously&lt;/h2&gt;
&lt;p&gt;Whatever you’re doing on the Web, it’ll be standing on the shoulders of HTML. Keep your markup tidy. It will save you from surprises, and improve the user experience. These things will never go out of fashion 😉&lt;/p&gt;
			</content></entry><entry><title>CSS lazy loading is kinda broken in Safari</title><link href="https://pepelsbey.dev/articles/lazy-loading-safari/"/><updated>2023-02-06T00:00:00Z</updated><id>https://pepelsbey.dev/articles/lazy-loading-safari/</id><content type="html">&lt;p&gt;I know, it’s a strong statement. You might even call it clickbait. But hear me out! Remember this old trick that allowed us to load only critical CSS and defer the rest? Yes, the one that used &lt;code&gt;media=&amp;quot;print&amp;quot;&lt;/code&gt; to change the value to &lt;code&gt;all&lt;/code&gt; in the &lt;code&gt;onload&lt;/code&gt; event.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;link&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	rel&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;critical.css&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;link&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	rel&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;deferred.css&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	media&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;print&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	onload&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;media&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;all&#39;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;noscript&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;link&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;		rel&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;		href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;deferred.css&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;noscript&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It first caught my eye back in 2019 in &lt;a href=&quot;https://www.filamentgroup.com/lab/load-css-simpler/&quot;&gt;The Simplest Way to Load CSS Asynchronously&lt;/a&gt; article by Scott Jehl. It was simple, elegant, and fail-proof: the &lt;code&gt;&amp;lt;noscript&amp;gt;&lt;/code&gt; element would make sure that CSS is loaded even if JavaScript is disabled.&lt;/p&gt;
&lt;p&gt;And it’s still used today, mainly to cut the cost of blocking resources and improve performance. I stumbled upon it again in the docs for the newly-released &lt;a href=&quot;https://github.com/11ty/eleventy-plugin-bundle&quot;&gt;eleventy-plugin-bundle&lt;/a&gt;, a nice helper for Eleventy.&lt;/p&gt;
&lt;p&gt;I looked at the example in the docs and suddenly realized that it won’t work in Safari. Why? Because of the behavior I talked about in the recent &lt;a href=&quot;https://pepelsbey.dev/articles/conditionally-adaptive/#a-catch&quot;&gt;Conditionally adaptive CSS&lt;/a&gt; article.&lt;/p&gt;
&lt;p&gt;Long story short, although Safari does load the CSS with non-matching &lt;code&gt;media=&amp;quot;print&amp;quot;&lt;/code&gt; at the low priority, it still &lt;em&gt;might&lt;/em&gt; block the rendering until the very last CSS file is loaded. And this is exactly what this trick relies on: a quick rendering of the critical CSS, not blocked by the deferred styles.&lt;/p&gt;
&lt;p&gt;Turns out, it’s a bit more complicated. Let’s take a look at the demo.&lt;/p&gt;
&lt;h2&gt;Lazy demo&lt;/h2&gt;
&lt;p&gt;Here’s the slightly modified demo from the previous article I used for testing:&lt;/p&gt;
&lt;iframe src=&quot;https://pepelsbey.dev/articles/lazy-loading-safari/demo/index.html&quot; height=&quot;280&quot; loading=&quot;lazy&quot; title=&quot;Demo with the word “lazy” repeated on the violet background.&quot;&gt;&lt;/iframe&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;link&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	rel&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;deferred.css&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	media&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;not all&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	onload&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;this&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;.&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;media&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;all&#39;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The markup is almost the same, but I replaced &lt;code&gt;print&lt;/code&gt; value with &lt;code&gt;not all&lt;/code&gt;, which is the opposite of the &lt;code&gt;all&lt;/code&gt; &lt;a href=&quot;https://www.w3.org/TR/mediaqueries-4/#mq-not&quot;&gt;per spec&lt;/a&gt; and makes much more sense here. There’s no real content on the page, it’s just a demo, after all. The &lt;em&gt;critical.css&lt;/em&gt; contains the background color and size for the image that will come next:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	background-color&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;#9073c9&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	background-size&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;327&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; 280&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And here comes the image, in the &lt;em&gt;deferred.css:&lt;/em&gt;&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	background-image&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;data:image/png;base64,…&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Like in the previous article, I used over-bloated base64-encoded PNG as a background image to make styles heavier. To make things even more interesting, I launched the demo using &lt;a href=&quot;https://www.npmjs.com/package/slow-static-server&quot;&gt;slow-static-server&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is how it loads in Chrome:&lt;/p&gt;
&lt;figure&gt;
	&lt;video controls=&quot;&quot; muted=&quot;&quot; playsinline=&quot;&quot; preload=&quot;none&quot; poster=&quot;https://pepelsbey.dev/articles/lazy-loading-safari/video/chrome.jpg&quot; width=&quot;2048&quot; height=&quot;1152&quot;&gt;
		&lt;source src=&quot;https://pepelsbey.dev/articles/lazy-loading-safari/video/chrome.mp4&quot; type=&quot;video/mp4&quot; /&gt;
	&lt;/video&gt;
&lt;/figure&gt;
&lt;ol&gt;
&lt;li&gt;The critical CSS is loaded instantly, and we see the background color.&lt;/li&gt;
&lt;li&gt;The deferred CSS loads for 23 seconds, and only then we see the background image.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In Safari, we can finally see what the title of this article is all about:&lt;/p&gt;
&lt;figure&gt;
	&lt;video controls=&quot;&quot; muted=&quot;&quot; playsinline=&quot;&quot; preload=&quot;none&quot; poster=&quot;https://pepelsbey.dev/articles/lazy-loading-safari/video/safari.jpg&quot; width=&quot;2048&quot; height=&quot;1152&quot;&gt;
		&lt;source src=&quot;https://pepelsbey.dev/articles/lazy-loading-safari/video/safari.mp4&quot; type=&quot;video/mp4&quot; /&gt;
	&lt;/video&gt;
&lt;/figure&gt;
&lt;p&gt;It takes 23 seconds to show anything at all. And we get background color and image at the same time, which makes lazy loading useless. Definitely broken, if you ask me. So, this is it, right? Well, it was until I got feedback from WebKit engineers: apparently, this behavior depends on the length of the content 🤔&lt;/p&gt;
&lt;h2&gt;Full body&lt;/h2&gt;
&lt;p&gt;You don’t really expect browsers to load CSS differently depending on the length of the content, do you? Well, it seems like this is exactly what’s happening in Safari. If the length of the page’s &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; is 200 characters or less, the deferred CSS will block the rendering.&lt;/p&gt;
&lt;p&gt;Yes, I manually entered 200 zeroes, and the demo still worked the same, but when I entered one more, it suddenly got fixed. It’s funny that spaces don’t count, only characters. I’m sorry, but I had to try this: it takes only 34 🤡 emojis to make it work. Some Unicode magic, I guess.&lt;/p&gt;
&lt;figure&gt;
	&lt;video controls=&quot;&quot; muted=&quot;&quot; playsinline=&quot;&quot; preload=&quot;none&quot; poster=&quot;https://pepelsbey.dev/articles/lazy-loading-safari/video/funny.jpg&quot; width=&quot;2048&quot; height=&quot;1152&quot;&gt;
		&lt;source src=&quot;https://pepelsbey.dev/articles/lazy-loading-safari/video/funny.mp4&quot; type=&quot;video/mp4&quot; /&gt;
	&lt;/video&gt;
&lt;/figure&gt;
&lt;p&gt;The good news is that this behavior just got fixed &lt;a href=&quot;https://github.com/WebKit/WebKit/pull/9746&quot;&gt;in the PR to the WebKit engine&lt;/a&gt; the next day I published the first version of this article. We might see the updated behavior in &lt;a href=&quot;https://developer.apple.com/safari/technology-preview/&quot;&gt;Safari TP&lt;/a&gt; very soon! But the question remains…&lt;/p&gt;
&lt;h2&gt;What now?&lt;/h2&gt;
&lt;p&gt;I’d be careful with this lazy-loading technique. Fortunately, it works fine in Safari with regular websites, with content usually more than 200 characters. But I can imagine a SPA that would look like this:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;root&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This content won’t pass the 200 characters threshold for regular CSS loading, and the critical CSS will be blocked. So what? I’ve seen deferred styles used for lazy-loading base64-encoded fonts and images, which is a bad idea in general and does more harm than good.&lt;/p&gt;
&lt;p&gt;But &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; is not the only way to load CSS with media conditions. There’s also &lt;code&gt;@import&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;link&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	rel&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;dark.css&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	media&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;(prefers-color-scheme: dark)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;style&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;	@import&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; url&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;dark.css&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;) (prefers-color-scheme: dark);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;style&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both of these are supposed to work the same way, but not a single browser prioritizes CSS loading for &lt;code&gt;@import&lt;/code&gt; media conditions for now. But there’s an &lt;a href=&quot;https://bugs.chromium.org/p/chromium/issues/detail?id=1001078&quot;&gt;issue for Chromium&lt;/a&gt; not getting enough attention. You know what to do if you want to see this fixed: press the ⭐️&lt;/p&gt;
			</content></entry><entry><title>A CSS challenge: skewed highlight</title><link href="https://pepelsbey.dev/articles/skewed-highlight/"/><updated>2022-12-26T00:00:00Z</updated><id>https://pepelsbey.dev/articles/skewed-highlight/</id><content type="html">&lt;p&gt;I often challenge myself to see if something is possible to implement in a sensible way or to play around with new Web platform features. I end up with a demo but rarely share it with anyone. Now that I have a blog, nothing stops me from doing it publicly! It sounds a bit creepy, but there you go.&lt;/p&gt;
&lt;p&gt;Recently Sacha Greif challenged his followers &lt;a href=&quot;https://twitter.com/SachaGreif/status/1603900122592870400&quot;&gt;with a tweet&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;CSS challenge: I’m curious, can you do this kind of highlighter effect using only CSS, while adapting to text changes?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;And a picture of some nice-looking headline with a highlight:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pepelsbey.dev/articles/skewed-highlight/images/source.png&quot; alt=&quot;The multiline heading “The European Accessibility Act—A milestone for digital accessibility” with the first two lines highlighted with skewed yellow rectangles.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;As you can see, this highlight is rather custom for this specific text, mainly because of the slight rotation. And though everything is possible &lt;a href=&quot;https://codepen.io/miocene/full/ZEJxzgR&quot;&gt;when you’re drawing with CSS&lt;/a&gt;, I wanted to create something more or less practical.&lt;/p&gt;
&lt;p&gt;So, I took the bait and started coding.&lt;/p&gt;
&lt;h2&gt;Grotesk font&lt;/h2&gt;
&lt;p&gt;First, I needed to find a font used in the picture. Not the exact one, but something similar and available on Google Fonts. I used “The European Accessibility” as a sample and “grotesk” as a keyword. I got lucky as the first result &lt;a href=&quot;https://fonts.google.com/specimen/Hanken+Grotesk&quot;&gt;Hanken Grotesk&lt;/a&gt; looked pretty close, especially with the bolder 900 weight.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pepelsbey.dev/articles/skewed-highlight/images/fonts.png&quot; alt=&quot;Google Fonts main page with the “grotesk” as a search keyword and “The European Accessibility” as a sample.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I picked the colors from the picture, played with the line height, and got a good starting point. Be careful with viewport units for the font size, though. I used it here only for demo purposes. In real life, it would stop users from scaling the text, which is an accessibility concern.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	margin&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	display&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;grid&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	place-items&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;center&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	background-color&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;#f6f6ec&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	font-family&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;Hanken Grotesk&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;sans-serif&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	max-width&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;18&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;ch&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	color&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;#142847&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	line-height&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;1.1&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	font-size&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;7.5&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;vw&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;iframe src=&quot;https://pepelsbey.dev/articles/skewed-highlight/demo/type.html&quot; height=&quot;500&quot; loading=&quot;lazy&quot; title=&quot;The heading with the Hanken Grotesk font and text styles applied.&quot;&gt;&lt;/iframe&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	The European Accessibility Act—&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;br&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	A milestone for digital accessibility&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;h1&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For similarity’s sake, I also had to use &lt;code&gt;&amp;lt;br&amp;gt;&lt;/code&gt; in combination with &lt;code&gt;max-width: 18ch&lt;/code&gt;, though it’s usually not a good idea in real-life cases. But I think I managed to find a balance between practicality and the original text look.&lt;/p&gt;
&lt;h3&gt;Subsetting&lt;/h3&gt;
&lt;p&gt;To use Hanken Grotesk font in the demo, I took the usual HTML snippet from Google Fonts: two preconnects and the font stylesheet.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;link&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	rel&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;preconnect&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;https://fonts.googleapis.com&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;link&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	rel&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;preconnect&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;https://fonts.gstatic.com&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	crossorigin&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;link&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	rel&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;https://fonts.googleapis.com/css2?…&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But since it was just a single line of text, not the whole website, I added one more &lt;code&gt;text&lt;/code&gt; GET parameter to the URL to deliver a custom font version containing only needed glyphs. 3 KB instead of 12 KB, not bad! You can also subset fonts with &lt;a href=&quot;https://github.com/zachleat/glyphhanger&quot;&gt;glyphhanger&lt;/a&gt; if you prefer to serve them yourself, as I do. But that would be too much for a simple demo.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span&gt;text=aAbcdeEfghilmnoprstTuy%20%E2%80%94&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This weird-looking value is the text of our headline, but with the letters sorted and duplicates removed. I also URL-encoded some symbols: &lt;code&gt;%20&lt;/code&gt; is the space, and &lt;code&gt;%E2%80%94&lt;/code&gt; is the em dash. You don’t need to sort or deduplicate the value for such a simple case, but why not.&lt;/p&gt;
&lt;p&gt;Alright, enough with the text. Let’s see how I highlighted it.&lt;/p&gt;
&lt;h2&gt;Remarkable mark&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://html.spec.whatwg.org/multipage/text-level-semantics.html#the-mark-element&quot;&gt;The &lt;code&gt;&amp;lt;mark&amp;gt;&lt;/code&gt; element&lt;/a&gt; is the obvious choice to highlight anything in a text. It even comes with &lt;mark&gt;built-in styles&lt;/mark&gt; similar to what we have in the picture. But not &lt;em&gt;that&lt;/em&gt; similar, so I replaced it with a nicer yellow. I also had to inherit the color from the header since it’s set explicitly to black in the browser styles.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;mark&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	background-color&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;#f8db75&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	color&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;inherit&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It looked pretty close already, but the whole point of this challenge was the side angles.&lt;/p&gt;
&lt;iframe src=&quot;https://pepelsbey.dev/articles/skewed-highlight/demo/mark.html&quot; height=&quot;500&quot; loading=&quot;lazy&quot; title=&quot;The heading with the multi-line styled mark tag highlighting the first part.&quot;&gt;&lt;/iframe&gt;
&lt;h2&gt;Skewed background&lt;/h2&gt;
&lt;p&gt;Unfortunately, there’s no way to skew the background, so I had to construct it from multiple parts using gradients: left and right rectangles with a diagonal gradient and a part in between with just a fill.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pepelsbey.dev/articles/skewed-highlight/images/sliced.svg&quot; alt=&quot;Yellow rectangle skewed to the right with contour overlay splitting it into three parts, from left to right. The first part is narrow and splits diagonally: the top left is transparent, and the bottom right is yellow. The second middle part is wide and entirely yellow. The third part is narrow and splits diagonally: the top left is yellow, and the bottom right is transparent.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Since the gradient replaces the background color, I set it to transparent. Then I passed three linear gradients separated by commas to the &lt;code&gt;background-image&lt;/code&gt; property.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;mark&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	background-color&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;transparent&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	background-image&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;		linear-gradient&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;			to&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; bottom&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; right&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;			transparent&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; 50&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;			#f8db75&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; 50&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; 100&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;%&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;		linear-gradient&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;			to&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; right&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;			#f8db75&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;			#f8db75&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;		linear-gradient&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;			to&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; top&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; left&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;			transparent&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; 50&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;			#f8db75&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; 50&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;%&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;The first goes to the bottom right, from transparent to yellow, with a 50% hard stop.&lt;/li&gt;
&lt;li&gt;The second goes to the right, but it could’ve been any direction since it’s just a yellow fill.&lt;/li&gt;
&lt;li&gt;The third goes to the top left, just like the first one but in the opposite direction.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The gradients with specific directions like &lt;code&gt;to bottom right&lt;/code&gt; go precisely from one corner to another, no matter the element’s shape. This was very convenient because the skew could be adjusted by changing the rectangle’s width.&lt;/p&gt;
&lt;p&gt;It was just a first step: by default, gradients overlap each other and repeat all over the place. To position them as on the scheme above, I needed to shape them with &lt;code&gt;background-size&lt;/code&gt; and &lt;code&gt;background-position&lt;/code&gt; and, of course, stop the repeating.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;mark&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	background-size&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;		0.25&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;		calc&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;100&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt; -&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; 0.25&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt; *&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; 2&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt; +&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;		0.25&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;em&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	background-position&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;		left&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; center&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;		center&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;		right&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; center&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	background-repeat&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;no-repeat&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Like in the previous case, I used multiple values separated by commas. &lt;code&gt;0.25em&lt;/code&gt; is the width of the side rectangles, and &lt;code&gt;1em&lt;/code&gt; is the height. The width of the middle rectangle is the width of the whole element minus the width of the sides. I had to add &lt;code&gt;1px&lt;/code&gt; to the width to make the middle rectangle overlap the sides because, otherwise, at specific font sizes and page scaling, there would be a small gap in Chrome and Firefox.&lt;/p&gt;
&lt;p&gt;Positioning background was fairly simple:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Left side goes to the left and stays centered vertically.&lt;/li&gt;
&lt;li&gt;Middle part stays in the center: a single keyword means &lt;code&gt;center center&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Right side goes to the right and stays centered vertically, too.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Once the background was done, I did a minor refactoring and used a few custom properties to make the highlight easily adjustable. But first, let’s look at the result!&lt;/p&gt;
&lt;iframe src=&quot;https://pepelsbey.dev/articles/skewed-highlight/demo/almost.html&quot; height=&quot;500&quot; loading=&quot;lazy&quot; title=&quot;The heading with the side angles at the beginning and the end of the whole highlight.&quot;&gt;&lt;/iframe&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;mark&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#FFAB70;--shiki-light:#E36209&quot;&gt;	--mark-color&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;#f8db75&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#FFAB70;--shiki-light:#E36209&quot;&gt;	--mark-skew&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;0.25&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#FFAB70;--shiki-light:#E36209&quot;&gt;	--mark-height&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#FFAB70;--shiki-light:#E36209&quot;&gt;	--mark-overlap&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;0.3&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;em&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	margin-inline&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;calc&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FFAB70;--shiki-light:#E36209&quot;&gt;--mark-overlap&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; -1&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	padding-inline&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FFAB70;--shiki-light:#E36209&quot;&gt;--mark-overlap&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	background-color&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;transparent&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	background-image&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;		linear-gradient&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;			to&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; bottom&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; right&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;			transparent&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; 50&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;			var&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FFAB70;--shiki-light:#E36209&quot;&gt;--mark-color&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;50&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;%&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;		linear-gradient&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;			var&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FFAB70;--shiki-light:#E36209&quot;&gt;--mark-color&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;			var&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FFAB70;--shiki-light:#E36209&quot;&gt;--mark-color&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;		linear-gradient&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;			to&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; top&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; left&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;			transparent&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; 50&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;			var&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FFAB70;--shiki-light:#E36209&quot;&gt;--mark-color&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;50&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;%&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	background-size&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;		var&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FFAB70;--shiki-light:#E36209&quot;&gt;--mark-skew&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FFAB70;--shiki-light:#E36209&quot;&gt;--mark-height&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;		calc&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;100&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt; -&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; var&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FFAB70;--shiki-light:#E36209&quot;&gt;--mark-skew&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;*&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; 2&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt; +&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FFAB70;--shiki-light:#E36209&quot;&gt;--mark-height&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;		var&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FFAB70;--shiki-light:#E36209&quot;&gt;--mark-skew&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;var&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FFAB70;--shiki-light:#E36209&quot;&gt;--mark-height&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	background-position&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;		left&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; center&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;		center&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;		right&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; center&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	background-repeat&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;no-repeat&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	color&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;inherit&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To make it closer to the picture, I extended the sides to slightly overlap the em dash. I added &lt;code&gt;padding-inline&lt;/code&gt; for padding on the sides and &lt;code&gt;margin-inline&lt;/code&gt; with the same value but negative to compensate for the padding.&lt;/p&gt;
&lt;p&gt;It looked close at that point, apart from one tiny detail.&lt;/p&gt;
&lt;h2&gt;Decoration break&lt;/h2&gt;
&lt;p&gt;From the beginning, I assumed that the whole highlight is a single element that breaks into multiple lines or stays in a single line, depending on the text width. But in the picture, we have side angles on every line! Did it mean I had to use &lt;code&gt;&amp;lt;mark&amp;gt;&lt;/code&gt; elements for every line? Fortunately, no.&lt;/p&gt;
&lt;p&gt;There’s a way to control how the box breaks into multiple lines or, to be precise, how its decoration breaks. It’s conveniently called &lt;code&gt;box-decoration-break&lt;/code&gt;, and the &lt;code&gt;clone&lt;/code&gt; value did precisely what I needed.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;mark&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	-webkit-box-decoration-break&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;clone&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	box-decoration-break&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;clone&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I had to use the &lt;code&gt;-webkit-&lt;/code&gt; prefix for it to work in Chrome and Safari, but the result was just stunning: every line of the &lt;code&gt;&amp;lt;mark&amp;gt;&lt;/code&gt; element was decorated like its own element.&lt;/p&gt;
&lt;iframe src=&quot;https://pepelsbey.dev/articles/skewed-highlight/demo/final.html&quot; height=&quot;500&quot; loading=&quot;lazy&quot; title=&quot;The heading with the side angles at the beginning and the end of every separate part of the highlight.&quot;&gt;&lt;/iframe&gt;
&lt;hr /&gt;
&lt;p&gt;As &lt;a href=&quot;https://kizu.ru/&quot;&gt;Roman Komarov&lt;/a&gt; once said (I hope I’m not making it up): if you see a challenge, don’t look at the implementations. Try to code it yourself and only then compare it. This way, you’ll learn more. I guess it’s too late with this article, but keep this idea in mind for the next challenge!&lt;/p&gt;
			</content></entry><entry><title>CSS and JavaScript as first-class citizens in Eleventy</title><link href="https://pepelsbey.dev/articles/eleventy-css-js/"/><updated>2022-11-25T00:00:00Z</updated><id>https://pepelsbey.dev/articles/eleventy-css-js/</id><content type="html">&lt;p&gt;When I started to build my first website on &lt;a href=&quot;https://www.11ty.dev/&quot;&gt;Eleventy&lt;/a&gt; around 2019, I had to decide how to deal with the HTML, CSS, and JavaScript post-processing. By that time, I had gotten used to the convenience of the modular approach and automation. So it wasn’t an option to just copy files from &lt;em&gt;src&lt;/em&gt; to &lt;em&gt;dist&lt;/em&gt;. I needed them stitched, modified, and minified.&lt;/p&gt;
&lt;p&gt;By then, I got over the preprocessors like Sass, so I needed some light post-processing for vanilla-flavored CSS and JS. I looked through the &lt;a href=&quot;https://www.11ty.dev/docs/starter/&quot;&gt;Eleventy starter projects&lt;/a&gt; but couldn’t find anything that would make sense. It was clear that early adopters of Eleventy were struggling with processing resources too.&lt;/p&gt;
&lt;p&gt;I came up with a solution that worked for me for a few years, but I recently took the next step, finally making CSS and JS first-class citizens in Eleventy for me 😎&lt;/p&gt;
&lt;h2&gt;Vanilla all the way&lt;/h2&gt;
&lt;p&gt;The first solution I came up with was pretty straightforward. Before setting up any post-processing, I built a system that didn’t require any: I just linked &lt;em&gt;index.css&lt;/em&gt; and &lt;em&gt;index.js&lt;/em&gt; files to my HTML pages that, in turn, were importing other blocks/modules:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#6A737D;--shiki-light:#6A737D&quot;&gt;/* index.css */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;@import&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt; &#39;blocks/page.css&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;@import&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt; &#39;blocks/header.css&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;@import&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt; &#39;blocks/content.css&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the JS case, you also need to add &lt;code&gt;type=&amp;quot;module&amp;quot;&lt;/code&gt; to your &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; to make it work. Oh, and for some reason, browsers need a &lt;code&gt;./&lt;/code&gt; prefix for relative ESM imports, but otherwise, it looks pretty much the same:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#6A737D;--shiki-light:#6A737D&quot;&gt;/* index.js */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt; &#39;./modules/menu.js&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt; &#39;./modules/video.js&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;import&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt; &#39;./modules/podcast.js&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And you know what? It just worked right in the browser. Yes, it wasn’t ideal from the performance and compatibility perspectives, but that was good enough for local development. And it was super quick too. When modern bundlers argue which one’s faster, I often think that the fastest is the one that’s not running at all 😉&lt;/p&gt;
&lt;p&gt;So when I was starting a project for local development via &lt;code&gt;npm start&lt;/code&gt;, it was Eleventy running its server, watching for changes, and copying CSS and JS files when needed. It would never work for Sass or TypeScript, but I always try to pick the simplest tools for simple tasks.&lt;/p&gt;
&lt;p&gt;As for the HTML, Eleventy has been taking care of it for me, generating markup from Markdown, Nunjucks, and data files. On top of that, using built-in &lt;code&gt;addTransform&lt;/code&gt;, I added a minification processing with &lt;a href=&quot;https://github.com/terser/html-minifier-terser&quot;&gt;html-minifier-terser&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; htmlmin&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; require&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;html-minifier-terser&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;config.&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;addTransform&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;html-minify&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;, (&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FFAB70;--shiki-light:#E36209&quot;&gt;content&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FFAB70;--shiki-light:#E36209&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;	if&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; (path &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;&amp;#x26;&amp;#x26;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; path.&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;endsWith&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;.html&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;)) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;		return&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; htmlmin.&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;minify&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;			content, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;				collapseBooleanAttributes: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;				collapseWhitespace: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;				decodeEntities: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;				includeAutoGeneratedTags: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;false&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;				removeComments: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;			}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;	return&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; content;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;⚠️ This is a part of the bigger Eleventy config, &lt;a href=&quot;https://www.11ty.dev/docs/config/&quot;&gt;see the docs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;And though I liked this approach a lot for local development, I needed some post-processing for CSS and JS to make the code production-ready.&lt;/p&gt;
&lt;h2&gt;Second pass&lt;/h2&gt;
&lt;p&gt;Running &lt;code&gt;npm run build&lt;/code&gt; would give me a fully-functional website in the &lt;em&gt;dist&lt;/em&gt; folder. I only needed to post-process some files before the deployment. As I always liked the &lt;a href=&quot;https://gulpjs.com/&quot;&gt;Gulp&lt;/a&gt; for its simplicity, it was an obvious choice. I took &lt;a href=&quot;https://postcss.org/&quot;&gt;PostCSS&lt;/a&gt; with some plugins for CSS and &lt;a href=&quot;https://rollupjs.org/&quot;&gt;Rollup&lt;/a&gt; with &lt;a href=&quot;https://babeljs.io/&quot;&gt;Babel&lt;/a&gt; and &lt;a href=&quot;https://terser.org/&quot;&gt;Terser&lt;/a&gt; plugins for JS.&lt;/p&gt;
&lt;p&gt;Here’s an example of a Gulp task for CSS. Don’t focus on the list of plugins just yet. We’ll have a closer look at them a bit later.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; styles&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;	return&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; gulp.&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;src&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;dist/styles/index.css&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		.&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;pipe&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;postcss&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;([&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;			require&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;postcss-import&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;			require&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;postcss-media-minmax&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;			require&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;autoprefixer&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;			require&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;postcss-csso&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		]))&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		.&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;pipe&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(gulp.&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;dest&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;dist&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;));&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I enjoyed the whole system for a while because I didn’t have to build and support the Eleventy/Gulp coupling. But at the same time, the additional build step bothered me. I’ve been trying different approaches, from complicated npm scripts to the various &lt;a href=&quot;https://vitejs.dev/&quot;&gt;Vite&lt;/a&gt; plugins for Eleventy, but they all didn’t make me happy.&lt;/p&gt;
&lt;h2&gt;Custom Handlers&lt;/h2&gt;
&lt;p&gt;But then I noticed something interesting in the &lt;a href=&quot;https://github.com/11ty/eleventy/releases/tag/v1.0.0&quot;&gt;Eleventy v1.0.0 changelog&lt;/a&gt; 😲&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Custom File Extension Handlers: applications and plugins can now add their own template types and tie them to a file extension.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It didn’t exactly say, “now you can post-process your CSS and JS,” but later, I discovered an &lt;a href=&quot;https://www.11ty.dev/docs/languages/custom/#example-add-sass-support-to-eleventy&quot;&gt;example in the documentation&lt;/a&gt; adding Sass support to Eleventy. That was precisely what I needed! Not the Sass, but built-in support for processing resources.&lt;/p&gt;
&lt;p&gt;Unlike the previous approach, Eleventy takes care of all CSS and JS resources this time. Not only for a production build but also during development. The closer your development build to the production one, the sooner you can spot any problems. But it only works if build time and live reload are fast enough not to get in the way during development.&lt;/p&gt;
&lt;p&gt;Every library got its quirks: different APIs, sync/async behavior, etc. So it took me some time to figure out how to make custom handlers work with the libraries I used to process CSS and JS. Like in the Gulp case, I chose &lt;a href=&quot;https://postcss.org/&quot;&gt;PostCSS&lt;/a&gt; for CSS. For JS, I decided to try &lt;a href=&quot;https://esbuild.github.io/&quot;&gt;esbuild&lt;/a&gt;, known for extremely fast build time, since I needed it to work for both production and development.&lt;/p&gt;
&lt;p&gt;Let’s dive into each custom handler to see how they work. I copied them from &lt;a href=&quot;https://github.com/pepelsbey/pepelsbey.dev/blob/main/eleventy.config.js&quot;&gt;this website’s Eleventy config&lt;/a&gt; and simplified it a little.&lt;/p&gt;
&lt;h3&gt;CSS&lt;/h3&gt;
&lt;p&gt;Remember the file structure? We have &lt;em&gt;src/styles/index.css&lt;/em&gt; file that’s importing other CSS files relative to its location. We need to combine them, process a bit, and output a single &lt;em&gt;dist/styles/index.css&lt;/em&gt; file.&lt;/p&gt;
&lt;p&gt;In the first step, I import all the packages I need to process my styles:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.npmjs.com/package/postcss&quot;&gt;postcss&lt;/a&gt; to process files using plugins&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.npmjs.com/package/postcss-import&quot;&gt;postcss-import&lt;/a&gt; to stitch all imported files together&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.npmjs.com/package/postcss-media-minmax&quot;&gt;postcss-media-minmax&lt;/a&gt; to polyfill modern Media Query syntax&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.npmjs.com/package/autoprefixer&quot;&gt;autoprefixer&lt;/a&gt; to prefix properties based on the &lt;a href=&quot;https://browserslist.dev/&quot;&gt;browserslist&lt;/a&gt; config&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.npmjs.com/package/postcss-csso&quot;&gt;postcss-csso&lt;/a&gt; to minimize the result&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The PostCSS &lt;a href=&quot;https://postcss.org/docs/postcss-plugins&quot;&gt;plugin ecosystem&lt;/a&gt; is quite extensive: you can build yourself the whole Sass or Stylus or use CSS from the future specs via &lt;a href=&quot;https://github.com/csstools/postcss-plugins/tree/main/plugin-packs/postcss-preset-env&quot;&gt;postcss-preset-env&lt;/a&gt; pack of plugins, but I prefer to write CSS that’s already supported in browsers and post-process it for better compatibility.&lt;/p&gt;
&lt;p&gt;By default, CSS files are not processed by Eleventy. To process them, we need to add CSS to the template formats list using the &lt;code&gt;addTemplateFormats&lt;/code&gt; method:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;config.&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;addTemplateFormats&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;css&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now Eleventy is ready to process our CSS files and output the result. Let’s configure this processing. Otherwise, it won’t be different from the passthrough copy. Using &lt;code&gt;addExtension&lt;/code&gt; we specify what files we’re going to process, including output file extension and async function that will be called with each file’s content and path.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;config.&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;addExtension&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;css&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	outputFileExtension: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;css&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	compile&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;async&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FFAB70;--shiki-light:#E36209&quot;&gt;content&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FFAB70;--shiki-light:#E36209&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#6A737D;--shiki-light:#6A737D&quot;&gt;		// Processing&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But we don’t need to process every CSS file that Eleventy could find in the &lt;em&gt;src&lt;/em&gt; folder. We need only the &lt;em&gt;index.css&lt;/em&gt; one, the rest CSS files will be imported into this one. That’s exactly what we‘re going to do next: filter out every other file that’s not the &lt;em&gt;index.css.&lt;/em&gt;&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;if&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; (path &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;!==&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt; &#39;./src/styles/index.css&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;	return&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can finally start processing our &lt;em&gt;index.css.&lt;/em&gt; And with the &lt;code&gt;path&lt;/code&gt; passed into the outer function, we can ask PostCSS to figure out the relative location of the rest of the files. It won’t just work otherwise. I learned it the hard way 🥲&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt; async&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;	let&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; output &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; postcss&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;([&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		postcssImport,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		postcssMediaMinmax,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		autoprefixer,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		postcssCsso,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	]).&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;process&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(content, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		from: path,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;	return&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; output.css;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our files will be stitched together, polyfilled, prefixed, and minified, just like we specified in the list of PostCSS plugins. The output will be passed to Eleventy, which will write it to the &lt;em&gt;dist/styles/index.css.&lt;/em&gt;&lt;/p&gt;
&lt;details&gt;
	&lt;summary&gt;The final result&lt;/summary&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; postcss&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; require&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;postcss&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; postcssImport&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; require&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;postcss-import&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; postcssMediaMinmax&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; require&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;postcss-media-minmax&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; autoprefixer&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; require&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;autoprefixer&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; postcssCsso&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; require&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;postcss-csso&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;config.&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;addTemplateFormats&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;css&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;config.&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;addExtension&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;css&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	outputFileExtension: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;css&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	compile&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;async&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FFAB70;--shiki-light:#E36209&quot;&gt;content&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FFAB70;--shiki-light:#E36209&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;		if&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; (path &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;!==&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt; &#39;./src/styles/index.css&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;			return&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;		return&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt; async&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;			let&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; output &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; postcss&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;([&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;				postcssImport,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;				postcssMediaMinmax,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;				autoprefixer,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;				postcssCsso,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;			]).&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;process&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(content, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;				from: path,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;			});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;			return&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; output.css;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;h3&gt;JavaScript&lt;/h3&gt;
&lt;p&gt;The same goes for JS files: we add template format, then process only &lt;em&gt;src/scripts/index.js&lt;/em&gt; using &lt;em&gt;esbuild&lt;/em&gt; with some simple options. This function will return the contents of all modules as a single file for Eleventy to output into the &lt;em&gt;dist&lt;/em&gt; folder. Unfortunately, &lt;em&gt;browserslist&lt;/em&gt; is not supported by &lt;em&gt;esbuild,&lt;/em&gt; but it seems like the &lt;code&gt;es2020&lt;/code&gt; target is similar to what I have there.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;return&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt; async&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;	let&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; output &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; esbuild.&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;build&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		target: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;es2020&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		entryPoints: [path],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		minify: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		bundle: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		write: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;false&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;	return&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; output.outputFiles[&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;].text;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;details&gt;
	&lt;summary&gt;The final result&lt;/summary&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;const&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; esbuild&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt; =&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; require&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;esbuild&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;config.&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;addTemplateFormats&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;js&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;config.&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;addExtension&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;js&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;, {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	outputFileExtension: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;js&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	compile&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;async&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FFAB70;--shiki-light:#E36209&quot;&gt;content&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FFAB70;--shiki-light:#E36209&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;) &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;		if&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; (path &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;!==&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt; &#39;./src/scripts/index.js&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;			return&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;		return&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt; async&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; () &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;=&gt;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;			let&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; output &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt; await&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; esbuild.&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;build&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;				target: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;es2020&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;				entryPoints: [path],&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;				minify: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;				bundle: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;true&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;				write: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;false&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;			});&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;			return&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; output.outputFiles[&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;].text;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/details&gt;
&lt;hr /&gt;
&lt;p&gt;Not sure if I convinced you, but I’m planning to use this approach for all my future projects based on Eleventy. I might even update the existing ones to make them more maintainable. Though I’m pretty sure there are many other use cases or ways to do it. I’m curious to see what you might come up with! 🙃&lt;/p&gt;
			</content></entry><entry><title>Condi&amp;shy;tionally adaptive CSS. Browser behavior that might improve your performance</title><link href="https://pepelsbey.dev/articles/conditionally-adaptive/"/><updated>2022-10-25T00:00:00Z</updated><id>https://pepelsbey.dev/articles/conditionally-adaptive/</id><content type="html">&lt;p&gt;There’s a component structure behind every website or app these days, hundreds of small files. Though when it comes to delivering resources, we often serve just a single file for every resource type: styles, scripts, and even &lt;a href=&quot;https://pepelsbey.dev/articles/svg-sprites/&quot;&gt;sprites for images&lt;/a&gt;. Everything gets squashed during the build process, including resources specific to certain resolutions or media conditions.&lt;/p&gt;
&lt;p&gt;And resources aren’t equal! For example, CSS is a blocking resource, and there’s nothing for a browser to render until every last byte of every CSS file is loaded. Why? The last line of a CSS file could overwrite something that came just before that. That’s your “C” in the CSS, which stands for “cascade.”&lt;/p&gt;
&lt;p&gt;We live in the era of responsive web design, and our websites are often ready to adapt to different viewports. Isn’t it wonderful? But why should users wait for irrelevant desktop styles when they load your site on mobile? 🤔&lt;/p&gt;
&lt;p&gt;Hold that thought, we’ll get back to it. Now let’s have a look at one popular website.&lt;/p&gt;
&lt;h2&gt;GOV.UK&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;https://pepelsbey.dev/articles/conditionally-adaptive/images/gov-uk.png&quot; alt=&quot;The main page of the GOV.UK website. The header says: Welcome to GOV.UK, the best place to find government services and information.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;If you open &lt;a href=&quot;https://www.gov.uk/&quot;&gt;GOV.UK&lt;/a&gt; website and peek into the DevTools Network panel, you’ll find four CSS files loaded, around 445 KB in total. The browser is supposed to wait for all of them to load before it can start rendering the page. That’s a lot of CSS, but I guess it’s all you’d ever need on this website.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;edefe0a8.css — 177.65 KB&lt;/li&gt;
&lt;li&gt;9618e981.css — 5.90 KB&lt;/li&gt;
&lt;li&gt;18950d6c.css — 201.04 KB&lt;/li&gt;
&lt;li&gt;59083555.css — 60.93 KB&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Apparently, only two are render-blocking, which makes the critical path around 67 KB shorter. What’s the trick? These two files are used only for printing and are linked with the proper &lt;code&gt;media&lt;/code&gt; attributes.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;link&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; rel&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;edefe0a8.css&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;link&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; rel&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;9618e981.css&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; media&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;print&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;link&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; rel&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;18950d6c.css&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;link&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; rel&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;59083555.css&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; media&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;print&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Browsers are smart enough not to prioritize resources that aren’t relevant to the current media conditions. When you’re in &lt;code&gt;media=&amp;quot;screen&amp;quot;&lt;/code&gt; and about to render something on the screen there’s no point in waiting for styles with &lt;code&gt;media=&amp;quot;print&amp;quot;&lt;/code&gt;, right?&lt;/p&gt;
&lt;p&gt;They could’ve bundled all four files, hiding print styles inside &lt;code&gt;@media print&lt;/code&gt;. But instead (intentionally or not) GOV.UK developers saved their users some time.&lt;/p&gt;
&lt;p&gt;When I discovered this behavior, I immediately asked myself…&lt;/p&gt;
&lt;h2&gt;What if?&lt;/h2&gt;
&lt;p&gt;What if we’d take the CSS bundle we’ve just built out of dozens of files and split it back into multiple parts? But this time, based on conditions where these parts are applicable. For example, we could split it into four files:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Base:&lt;/strong&gt; universal styles with fonts and colors&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mobile:&lt;/strong&gt; styles only for narrow viewports&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tablet:&lt;/strong&gt; styles only for medium viewports&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Desktop:&lt;/strong&gt; styles only for large viewports&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This would look like this:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;link&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; rel&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;base.css&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;link&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; rel&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;mobile.css&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;link&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; rel&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;tablet.css&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;link&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; rel&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;desktop.css&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But this won’t be enough, as we need to specify media conditions with the same &lt;code&gt;media&lt;/code&gt; attribute, using not just media types like &lt;code&gt;print&lt;/code&gt;, but media features. Yes, you can do that! 🤯&lt;/p&gt;
&lt;p&gt;Let’s say our tablet breakpoint starts at 768 px and ends at 1023 px. Everything below goes to mobile, and everything above goes to desktop.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;link&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	rel&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;base.css&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;link&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	rel&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;mobile.css&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	media&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;(max-width: 767px)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;link&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	rel&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;tablet.css&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	media&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;(min-width: 768px) and (max-width: 1023px)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;link&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	rel&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;desktop.css&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	media&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;(min-width: 1024px)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It would be much easier to write using the modern syntax, but I’d be careful for now: browsers are still &lt;a href=&quot;https://caniuse.com/css-media-range-syntax&quot;&gt;catching up on the support&lt;/a&gt;, and loading CSS is rather critical.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;(width &amp;lt; 768px)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(768px &amp;lt;= width &amp;lt; 1024px)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;(width &amp;gt;= 1024px)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When I opened this demo in a browser, I expected it to load only relevant files, for example, only &lt;em&gt;base.css&lt;/em&gt; and &lt;em&gt;mobile.css&lt;/em&gt; on mobile. But all four files were loaded, and I was disappointed at first. Only later I realized that it works, but in a much more sensible way 😲&lt;/p&gt;
&lt;h2&gt;Demo&lt;/h2&gt;
&lt;p&gt;To fully understand how it works, I built a demo page with four CSS files that paint the page with different background colors on different viewport widths. These files also have different sizes, so spotting them in the Network panel would be easier.&lt;/p&gt;
&lt;p&gt;The first &lt;em&gt;base.css&lt;/em&gt; is quite small, only 91 bytes:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;html&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;, &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	height&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;100&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	margin&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	background-position&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;center&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then goes &lt;em&gt;mobile.css&lt;/em&gt;, slightly bigger (16 KB), but only because I artificially made it so by inlining the bitmap “mobile” word with base64 as a background image.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;body&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	background-color&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;#ef875d&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	background-image&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;data:image/png;base64,…&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	background-size&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;511&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; 280&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I made both &lt;em&gt;tablet.css&lt;/em&gt; (83 KB) and &lt;em&gt;desktop.css&lt;/em&gt; (275 KB) even larger with bigger images inlined. You can play with the demo by resizing the window to get the idea. It’s going to help us understand how browsers prioritize CSS loading.&lt;/p&gt;
&lt;iframe src=&quot;https://pepelsbey.dev/articles/conditionally-adaptive/demo/index.html&quot; height=&quot;280&quot; loading=&quot;lazy&quot; title=&quot;Demo with words “mobile” on the red, “tablet” on the green, or “desktop” on the violet backgrounds, depending on the viewport width.&quot;&gt;&lt;/iframe&gt;
&lt;h2&gt;Priorities&lt;/h2&gt;
&lt;p&gt;Another little detail in the Network panel made me realize what was going on: the Priority column. You might have to enable it by right-clicking the table heading and choosing it from the list of available columns.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pepelsbey.dev/articles/conditionally-adaptive/images/network/desktop.png&quot; alt=&quot;Network panel in Chrome with the list of CSS files: base.css, desktop.css with “highest” priority, then mobile.css, tablet.css with “lowest” priority.&quot; /&gt;
CSS files aren’t equal either: desktop styles are more important than mobile ones.&lt;/p&gt;
&lt;p&gt;It took a surprisingly long time for this page to load, almost 12 seconds. It’s because I disabled the cache and throttled the network to “Slow 3G”. I keep it enabled in my DevTools because it reminds me of real-world network performance 😐&lt;/p&gt;
&lt;p&gt;You might’ve guessed where these priorities come from. All CSS files linked to the page are evaluated during HTML parsing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The ones with &lt;code&gt;media&lt;/code&gt; attribute relevant to the current conditions (or without one, which makes it &lt;code&gt;media=&amp;quot;all&amp;quot;&lt;/code&gt;) get loaded with the &lt;strong&gt;highest&lt;/strong&gt; priority.&lt;/li&gt;
&lt;li&gt;The ones with &lt;code&gt;media&lt;/code&gt; attribute irrelevant to the current conditions (like &lt;code&gt;media=&amp;quot;print&amp;quot;&lt;/code&gt; or &lt;code&gt;(width &amp;gt;= 1024px)&lt;/code&gt; on mobile) are still loaded, but with the &lt;strong&gt;lowest&lt;/strong&gt; priority.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the first case, I used desktop viewport width. What will happen if I load the same page in the mobile viewport? You’ll get the same files loaded but with different priorities: &lt;em&gt;base.css&lt;/em&gt; and &lt;em&gt;mobile.css&lt;/em&gt; are the highest priority.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pepelsbey.dev/articles/conditionally-adaptive/images/network/mobile.png&quot; alt=&quot;Network panel in Chrome with the list of CSS files: base.css, mobile.css with “highest” priority, then tablet.css, desktop.css with “lowest” priority.&quot; /&gt;
In smaller viewports, priorities change: mobile styles are more important than desktop ones.&lt;/p&gt;
&lt;p&gt;But it’s not just loading priority, it also affects the moment when the browser decides that it got everything it needs to render the page. Let’s go to the Performance panel in Chrome DevTools and see the waterfall and all the relevant page rendering events.&lt;/p&gt;
&lt;h2&gt;Rendering&lt;/h2&gt;
&lt;p&gt;The Performance panel is relatively complex compared to the Network one. I’m not going to go into details here, but to analyze performance, it’s essential to read waterfalls and see when certain events happen, not just what files are loaded and how heavy they are. Let’s unpack the basics of what’s going on here 🤓&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pepelsbey.dev/articles/conditionally-adaptive/images/performance/desktop.png&quot; alt=&quot;Performance panel in Chrome lists page resources in a waterfall. The page screenshot in a desktop viewport goes right after the desktop.css.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;This page is loaded in a desktop viewport, and the first thing we see on the waterfall is the blue line: this is our HTML document requested by the browser. At the point where it’s loaded and parsed, we get four parallel requests for CSS files, all with different lengths. The order is the same as in the Network panel: &lt;em&gt;base.css&lt;/em&gt; and &lt;em&gt;desktop.css&lt;/em&gt; go first.&lt;/p&gt;
&lt;p&gt;There’s a Frames panel below the waterfall showing when the browser paints something on the page. At the bottom of this panel, we have a group of flags marking some of the important events: &lt;strong&gt;FP&lt;/strong&gt; (First Paint), &lt;strong&gt;FCP&lt;/strong&gt; (First Contentful Paint), &lt;strong&gt;L&lt;/strong&gt; (Load), &lt;strong&gt;DCL&lt;/strong&gt; (DOMContentLoaded). In this case, everything happened at the same time, once &lt;em&gt;desktop.css&lt;/em&gt; was loaded.&lt;/p&gt;
&lt;p&gt;I used a desktop viewport to load this demo, so the browser had to wait for &lt;em&gt;base.css&lt;/em&gt; and &lt;em&gt;desktop.css&lt;/em&gt; to load before it could render anything (almost 12.5 seconds). And since &lt;em&gt;desktop.css&lt;/em&gt; is rather large and CSS files are loaded in parallel, the browser had a chance to load them all. So it’s hard to tell whether it worked better than just a single file with all the styles.&lt;/p&gt;
&lt;p&gt;Let’s load the same page in the mobile viewport, then.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pepelsbey.dev/articles/conditionally-adaptive/images/performance/mobile.png&quot; alt=&quot;Performance panel in Chrome lists page resources in a waterfall. The page screenshot in a mobile viewport goes right after the mobile.css.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Now it looks much more interesting! 😍 The order of CSS files is the same as we saw in the Network panel: &lt;em&gt;base.css&lt;/em&gt; and &lt;em&gt;mobile.css&lt;/em&gt; go first. But now it finally makes the difference: FP, FCP, and even DCL events happened right after the &lt;em&gt;mobile.css&lt;/em&gt; was loaded. The whole rendering took only 5 seconds, compared to 12.5 seconds in the previous case.&lt;/p&gt;
&lt;p&gt;The rest of the CSS files extend beyond the rendering events so that the page will be ready for any viewport changes. This is a rare event on mobile but often happens on desktop or tablets. Speaking of tablets, let’s see how it looks in a tablet viewport.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pepelsbey.dev/articles/conditionally-adaptive/images/performance/tablet.png&quot; alt=&quot;Performance in Chrome panel with the list of page resources in a waterfall. The screenshot of the page in a tablet viewport goes right after the tablet.css.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;No surprises here: the page is ready to be rendered once &lt;em&gt;tablet.css&lt;/em&gt; is loaded in 8 seconds, still faster than it would take for a single file with all the styles to load.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Like any other demo, this one shows the browser’s behavior in a specific case. I doubt that in your case &lt;em&gt;desktop.css&lt;/em&gt; would be dozen times bigger than &lt;em&gt;mobile.css&lt;/em&gt; and you’ll see a 7.5 seconds difference with the “Slow 3G” throttling. But at the same time, I can see the potential of this behavior, though it’s not widely known or used.&lt;/p&gt;
&lt;p&gt;It will also require you to write your CSS in a way that isolates styles for different viewports. That’s another idea for an article, by the way. The same goes for tooling: it’s not quite there yet. The closes thing I could find are &lt;a href=&quot;https://github.com/SassNinja/media-query-plugin&quot;&gt;Media Query plugin for Webpack&lt;/a&gt; and &lt;a href=&quot;https://github.com/SassNinja/postcss-combine-media-query&quot;&gt;Extract Media Query plugin for PostCSS&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Fortunately, there are some other simpler use cases apart from different viewports that we might start from. Oh, and there’s another catch, of course 🙄 But let’s talk about the use cases first.&lt;/p&gt;
&lt;h2&gt;Preferences&lt;/h2&gt;
&lt;p&gt;The list of &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/@media#media_features&quot;&gt;media features&lt;/a&gt; you can use in Media Queries extends beyond just viewport width and height. You can adapt your website to the user’s needs and preferences: color scheme, pixel density, reduced motion, etc. But not only that! With this new behavior in mind, we can make sure that only relevant styles are render-blocking.&lt;/p&gt;
&lt;h3&gt;Color scheme&lt;/h3&gt;
&lt;p&gt;One of the most popular media features these days is &lt;code&gt;prefers-color-scheme&lt;/code&gt;, which allows you to supply light and dark color schemes to match the user’s system preferences. In most cases, it’s used right in CSS as &lt;code&gt;@media&lt;/code&gt;, but it can also be used to link relevant CSS files conditionally.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;link&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	rel&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;light.css&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	media&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;(prefers-color-scheme: light)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;link&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	rel&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;dark.css&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	media&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;(prefers-color-scheme: dark)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And just like we’ve learned before, browsers will load &lt;em&gt;dark.css&lt;/em&gt; with the lowest priority in the case when the user prefers a light color scheme and vice versa. There’s a &lt;a href=&quot;https://web.dev/prefers-color-scheme/&quot;&gt;good article by Thomas Steiner&lt;/a&gt; diving deep into the dark mode, including a theme toggler that works with CSS files linked this way.&lt;/p&gt;
&lt;h3&gt;Pixel density&lt;/h3&gt;
&lt;p&gt;Sometimes we have to deal not with &lt;a href=&quot;https://pepelsbey.dev/articles/svg-sprites/&quot;&gt;beautiful vector graphics&lt;/a&gt;, but with raster images too. In this case, we often have different files for different pixel densities, for example, &lt;em&gt;icon.png&lt;/em&gt; and &lt;em&gt;icon@2x.png&lt;/em&gt;.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	display&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;block&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	width&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;24&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	height&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;24&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	background-image&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;icon.png&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;@media&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; (&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;min-resolution&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;dppx&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;	a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;		background-image&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;icon@2x.png&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;		background-size&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;24&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; 24&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These six lines of CSS specifically targeting high-density screens are usually bundled into the same file loaded for users with low-density screens. Fortunately, browsers are smart enough not to load high-density graphics. You guessed it, we can do it even better: split them into a separate file and load it with the lowest priority.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;link&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	rel&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;retina.css&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	media&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;(min-resolution: 2dppx)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This file will be loaded and applied immediately if the user decides to drag the browser window to a high-density screen. But it won’t block the initial rendering on a low-density screen.&lt;/p&gt;
&lt;h3&gt;Reduced motion&lt;/h3&gt;
&lt;p&gt;Animations and smooth transitions could improve user experience, but for some people they can be a source of distraction or discomfort. The media feature &lt;code&gt;prefers-reduced-motion&lt;/code&gt; allows you to wrap your heavy motion in &lt;code&gt;@media&lt;/code&gt; to give users a choice. By the way, “reduce” doesn’t mean “disable”, it’s up to you to create a comfortable environment and not sacrifice clarity at the same time.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;link&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	rel&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;stylesheet&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;animation.css&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;	media&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;(prefers-reduced-motion: no-preference)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead, you can put your motion-heavy styles into a separate file that will be loaded with the lowest priority when the user prefers reduced motion. The best way to achieve this would be to extract all the matching &lt;code&gt;@media&lt;/code&gt; during the build process.&lt;/p&gt;
&lt;h2&gt;A catch&lt;/h2&gt;
&lt;p&gt;There’s always a catch, isn’t it? 🥲 In this case, it’s the browser support: unfortunately, my tests show that Safari doesn’t support this behavior. Even in GOV.UK’s case, it blocks initial rendering until all the print styles are fully loaded. Fortunately, it doesn’t brake anything, but still, it’s a missed opportunity for performance improvement.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pepelsbey.dev/articles/conditionally-adaptive/images/safari/network.png&quot; alt=&quot;Network panel in Safari with the list of CSS files: base.css, mobile.css with “highest” priority, then tablet.css, desktop.css with “lowest” priority.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Interestingly, Safari sets the same priorities as Chrome, but it doesn’t change the overall behavior. And if you look at the Timeline panel, it becomes obvious: the first paint for mobile viewport happened only when &lt;em&gt;desktop.css&lt;/em&gt; was fully loaded.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pepelsbey.dev/articles/conditionally-adaptive/images/safari/timelines.png&quot; alt=&quot;Network panel in Safari with the list of CSS files: base.css, mobile.css with “highest” priority, then tablet.css, desktop.css with “lowest” priority.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;I filed &lt;a href=&quot;https://bugs.webkit.org/show_bug.cgi?id=243424&quot;&gt;a bug report in WebKit&lt;/a&gt; a few months ago, asking for a behavior change. They closed it as a duplicate in favor &lt;a href=&quot;https://bugs.webkit.org/show_bug.cgi?id=39455&quot;&gt;to this one&lt;/a&gt;. Please have a look, and if you have some real-life use cases, don’t hesitate to share them in the comments. So far, I have gotten some attention from WebKit engineers, but no actions yet.&lt;/p&gt;
&lt;p&gt;At this point, I’m very grateful to Firefox for supporting this behavior. Otherwise, it would be just a peculiar Chrome optimization that’s not worth relying on too much. Though it wasn’t easy to work with the Performance panel in Firefox, with the help of the Network panel’s throttling settings, I got a clear picture. In the mobile viewport, Firefox starts rendering before the &lt;em&gt;desktop.css&lt;/em&gt; is fully loaded.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pepelsbey.dev/articles/conditionally-adaptive/images/firefox/performance.png&quot; alt=&quot;Performance panel in Firefox with the film strip and a list of resources: the mobile.css loading is aligned with the first paint.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;And just to double-check that my readings are correct, I loaded the demo with a slow static server called &lt;a href=&quot;https://www.npmjs.com/package/slow-static-server&quot;&gt;slow-static-server&lt;/a&gt; 😬 and got the same results: Chrome and Firefox render the page in mobile viewport much earlier than Safari does.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;I think this might be a good opportunity to optimize the initial page rendering performance. And maybe even improve the way you adapt your styles to different media conditions. But if you’re not ready to invest in this optimization just yet, I’d encourage you to at least try exploring user preferences. There’s a nice “&lt;a href=&quot;https://codelabs.developers.google.com/codelabs/user-adaptive-interfaces/&quot;&gt;Build user-adaptive interfaces with preference Media Queries&lt;/a&gt;” codelab by Adam Argyle that will help you get started.&lt;/p&gt;
			</content></entry><entry><title>SVG sprites: old-school, modern, unknown, and forgotten</title><link href="https://pepelsbey.dev/articles/svg-sprites/"/><updated>2022-10-10T00:00:00Z</updated><id>https://pepelsbey.dev/articles/svg-sprites/</id><content type="html">&lt;p&gt;SVG sprites have been around for a while and are usually considered a default option for icons and some other vector graphics. I mean the ones that require inline SVG placeholders and could be styled via CSS. And while they’re giving us some unique features, they also have some drawbacks and aren’t the only available option. Let’s try to remember why we needed SVG sprites in the first place, then see what other less-known options are available and how they might be useful.&lt;/p&gt;
&lt;h2&gt;Why sprites&lt;/h2&gt;
&lt;p&gt;First of all, let’s all agree that sprites are a trick. You might call it a “technique” or a “tool”, but we mostly need it to work around some limitations. Back in the 8-bit game era, bitmap sprites were used to optimize memory performance: load all graphic resources into memory once and use them when needed.&lt;/p&gt;
&lt;p&gt;In the early Web days, sprites were used similarly, but to optimize network performance (limit the number of requests) and also work around the way browsers load resources. Consider this example: one background image should be replaced with another once the user hovers/focuses the link.&lt;/p&gt;
&lt;iframe src=&quot;https://pepelsbey.dev/articles/svg-sprites/demo/old-school/naive.html&quot; height=&quot;360&quot; loading=&quot;lazy&quot; title=&quot;Black contour cogwheel icon on a green background, on hover it becomes purple.&quot;&gt;&lt;/iframe&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	background-image&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;link.svg&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;:hover&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;:focus&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	background-image&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;hover.svg&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You might’ve noticed a little flick, especially if you’re on a slow network. But if it seemed fine to you, have a look at the DevTools Network panel before and after hover/focus. Browsers don’t preload CSS resources that are not needed for the initial rendering. It means that there will be a network request that might take a while or might not happen at all if the user went offline.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pepelsbey.dev/articles/svg-sprites/images/request.png&quot; alt=&quot;Two DevTools Network panels. First, before the hover, there are two resources: index.html and link.svg. Second, after the hover, there’s one more: hover.svg.&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In terms of limiting the number of requests using sprites, network performance is less relevant these days, but we still need some workaround to make sure that all resources are available for user interactions.&lt;/p&gt;
&lt;h2&gt;Old-school sprites&lt;/h2&gt;
&lt;p&gt;Let’s start with the old-school sprites or “true sprites”: we can stitch a bunch of pictures together in a single file, but show just one of them at a time through some viewport. Such sprites used to be mostly bitmaps back in the old days, but nothing’s stopping us from using vector graphics too.&lt;/p&gt;
&lt;p&gt;In the previous case, both icons were separate files, containing nothing but the same icon filled with different colors. Let’s put them together in a single file this time, right next to each other.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pepelsbey.dev/articles/svg-sprites/images/sprite.png&quot; alt=&quot;Two contour icons in a row: cogwheel in black, cogwheel in purple.&quot; /&gt;&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;svg&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; xmlns&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;http://www.w3.org/2000/svg&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; viewBox&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;0 0 48 24&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; fill&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;#0c0b1d&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; d&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;M19.43…&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; fill&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;#9874d3&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; d&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;M43.43…&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;svg&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Look at the &lt;code&gt;d&lt;/code&gt; attributes of every &lt;code&gt;&amp;lt;path&amp;gt;&lt;/code&gt; element, specifically on how they start: the number after the first &lt;code&gt;M&lt;/code&gt; letter is the coordinate of the first point. As you can see, they’re far apart: &lt;code&gt;M19&lt;/code&gt; and &lt;code&gt;M43&lt;/code&gt;. It means that icons in this sprite are drawn exactly where they need to be. I’m not suggesting that you’re supposed to read the rest of the curve (&lt;a href=&quot;https://youtu.be/9qen5CKjUe8&quot;&gt;a handful of people could do that&lt;/a&gt;), but understanding where it starts might become useful later.&lt;/p&gt;
&lt;h3&gt;Background images&lt;/h3&gt;
&lt;p&gt;The easiest way to put a decorative image on a page is to use the &lt;code&gt;background-image&lt;/code&gt; property. Seriously, you don’t always need to do complex things with your graphics, it’s usually more performant too. Let’s put our sprite in the background image and move its position to a certain coordinate to show the needed icon. There’s no need to set &lt;code&gt;background-position&lt;/code&gt; to &lt;code&gt;0 0&lt;/code&gt;, but I like to keep defaults visible when they’re about to change.&lt;/p&gt;
&lt;iframe src=&quot;https://pepelsbey.dev/articles/svg-sprites/demo/old-school/background.html&quot; height=&quot;360&quot; loading=&quot;lazy&quot; title=&quot;Black contour cogwheel icon on a green background, on hover it becomes purple.&quot;&gt;&lt;/iframe&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	background-image&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;sprite.svg&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	background-position&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	background-size&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;cover&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;:hover&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;:focus&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	background-position&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;-200&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Unlike bitmap sprites, vector ones are more flexible, since you can show icons of any size by scaling the background. But they come with some difficulties too: you have to calculate the &lt;code&gt;background-size&lt;/code&gt; value based on the resulting icon’s size and sprite’s dimensions. For example, a 72 × 24 sprite for a 200 × 200 icon will have a &lt;code&gt;background-size&lt;/code&gt; of 600 × 200. It’s simple math, but once the sprite is changed you might need to update the numbers.&lt;/p&gt;
&lt;p&gt;But in the case of a same-sized icon sprite positioned in a single row, using just the &lt;code&gt;cover&lt;/code&gt; value for &lt;code&gt;background-size&lt;/code&gt; would be sufficient. And once the icon is scaled, we’ll have to use the resulting icon’s size to move the background. In our case, it would be &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;−200px&lt;/code&gt; to switch between the icon’s states.&lt;/p&gt;
&lt;h3&gt;Content images too&lt;/h3&gt;
&lt;p&gt;Interestingly enough, you can use old-school sprites not only for background images but also for content images. I wouldn’t recommend using decorative images for icons because browsers might prioritize them too much during loading and your users will get “Save Image As…” and other irrelevant context menu items and behavior for your link. But for the sake of it, let’s try it 🤓&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;img&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;		src&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;sprite.svg&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;		width&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;200&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; height&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;200&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;		alt&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;Settings&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Styling in this case looks quite similar to the previous example, but with &lt;code&gt;object-fit&lt;/code&gt; and &lt;code&gt;object-position&lt;/code&gt; properties instead. Unlike &lt;code&gt;background-position&lt;/code&gt;, the default position here would be &lt;code&gt;50% 50%&lt;/code&gt;, so we’ll have to set it to &lt;code&gt;0 0&lt;/code&gt; to make it work the same way.&lt;/p&gt;
&lt;iframe src=&quot;https://pepelsbey.dev/articles/svg-sprites/demo/old-school/img.html&quot; height=&quot;360&quot; loading=&quot;lazy&quot; title=&quot;Black contour cogwheel icon on a green background, on hover it becomes purple.&quot;&gt;&lt;/iframe&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt; img&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	object-fit&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;cover&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	object-position&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;:hover&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt; img&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;:focus&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt; img&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	object-position&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;-200&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; 0&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using “true sprites” for bitmap images still makes some sense these days, but looking at the duplicated curves in the SVG sprite makes my heart hurt a little. It could be optimized with &lt;code&gt;&amp;lt;use&amp;gt;&lt;/code&gt; elements and custom fills, but building such a sprite and sharing colors between CSS and SVG wouldn’t be easy 😔&lt;/p&gt;
&lt;p&gt;That’s why we have a modern solution for SVG sprites.&lt;/p&gt;
&lt;h2&gt;Modern symbols&lt;/h2&gt;
&lt;p&gt;SVG became much more popular once developers realized that it’s not just another graphics format. You can change it via CSS, just like any other HTML element, but to make it work you have to put inline &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; in your markup, which increases the size of your document or JS bundle. Fortunately, this method got improved by SVG symbols and became a standard solution for icons.&lt;/p&gt;
&lt;h3&gt;Inline SVG&lt;/h3&gt;
&lt;p&gt;If you just need to change your SVG icon’s color fill via CSS, you can put it in your markup and call it a day. Feel free to get rid of the &lt;code&gt;xmlns&lt;/code&gt; attribute when your SVG is inlined, by the way. But don’t forget to add &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; attributes (otherwise your icon might take the whole page if your CSS will fail to load) and &lt;code&gt;aria-hidden=&amp;quot;true&amp;quot;&lt;/code&gt; to keep icons under the screen reader’s radar.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; aria-label&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;Settings&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;svg&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;		viewBox&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;0 0 24 24&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;		width&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;200&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; height&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;200&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;		aria-hidden&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; fill&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;currentcolor&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; d&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;M19.43…&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;svg&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;iframe src=&quot;https://pepelsbey.dev/articles/svg-sprites/demo/symbols/inline.html&quot; height=&quot;360&quot; loading=&quot;lazy&quot; title=&quot;Black contour cogwheel icon on a green background, on hover it becomes purple.&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;Such an icon would inherit the parent element’s text color because its &lt;code&gt;&amp;lt;path&amp;gt;&lt;/code&gt;’s fill is set to &lt;code&gt;currentcolor&lt;/code&gt;, some kind of a variable that carries, you guessed it, the current color. In this case, you don’t even have to style the actual SVG element.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	color&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;#0c0b1d&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;:hover&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;:focus&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	color&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;#9874d3&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But inline icons are not ideal. You can often rely on the browser cache when it comes to your document’s resources: styles, scripts, graphics, etc. But the document itself is rarely cached, meaning that your inline icons will add substantial overhead to every load of every page. Even in the SPA case, keeping your icons out is better to reduce the JS bundle size.&lt;/p&gt;
&lt;h3&gt;External SVG&lt;/h3&gt;
&lt;p&gt;To make all the paths &lt;em&gt;external&lt;/em&gt; to the document, we can put them together in a file organized in a special way. Let’s call it &lt;em&gt;sprite.svg&lt;/em&gt; and throw in another icon just to make it look like a library. Instead of the &lt;code&gt;&amp;lt;path&amp;gt;&lt;/code&gt; itself, we now have &lt;code&gt;&amp;lt;use&amp;gt;&lt;/code&gt; element that gets the symbol from the library by ID.&lt;/p&gt;
&lt;iframe src=&quot;https://pepelsbey.dev/articles/svg-sprites/demo/symbols/external.html&quot; height=&quot;360&quot; loading=&quot;lazy&quot; title=&quot;Black contour cogwheel icon on a green background, on hover it becomes purple.&quot;&gt;&lt;/iframe&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; aria-label&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;Settings&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;svg&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;		viewBox&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;0 0 24 24&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;		width&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;200&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; height&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;200&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;		aria-hidden&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;sprite.svg#favorite&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;svg&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Though we have to keep the inline SVG placeholder in the document, it drastically improves the footprint and allows browsers to cache the file. By the way, it’s time to get rid of the prefixed &lt;code&gt;xlink:href&lt;/code&gt;, simple &lt;code&gt;href&lt;/code&gt; has been more than enough for a while.&lt;/p&gt;
&lt;p&gt;How does this &lt;em&gt;sprite.svg&lt;/em&gt; look like? It contains our SVG icons wrapped in &lt;code&gt;&amp;lt;symbol&amp;gt;&lt;/code&gt; elements with unique IDs, so we could request only the needed ones.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;svg&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; xmlns&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;http://www.w3.org/2000/svg&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;symbol&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;settings&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; viewBox&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;0 0 24 24&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; fill&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;currentcolor&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; d&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;M19.43…&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;symbol&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;symbol&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;favorite&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; viewBox&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;0 0 24 24&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; fill&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;currentcolor&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; d&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;M16.5…&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;symbol&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;svg&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s have a look at the beginning of our curves in the &lt;code&gt;d&lt;/code&gt; attribute again. As you can see, they’re pretty close to each other: &lt;code&gt;M19&lt;/code&gt; and &lt;code&gt;M16&lt;/code&gt;. That’s because it’s not a “true sprite”, but rather a library of SVG symbols where icons are stacked on top of each other.&lt;/p&gt;
&lt;p&gt;Compared to old-school sprite, this symbol library is much easier to prepare: you don’t have to use a vector editor or recalculate paths, you just need to put icon files together and change &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; tags to &lt;code&gt;&amp;lt;symbol&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The downside of it is that we can’t use such icons in background images or content images, only with inline SVG placeholders. But the upside makes it worth the trouble: we can control our icon’s color fill right from CSS.&lt;/p&gt;
&lt;p&gt;This method is a built-in SVG feature useful for organizing complex vector documents. It also happens to be useful as a sprite-like workaround when combined with HTML and CSS. But there’s another rather unknown SVG feature that can be used similarly!&lt;/p&gt;
&lt;h2&gt;Unknown fragments&lt;/h2&gt;
&lt;p&gt;Let’s try one more time to use a “true” SVG sprite as a background image, with an anchor pointing to a specific icon in that sprite. Yes, the same thing that didn’t work previously. Wouldn’t it be nice to make it work? 🤔&lt;/p&gt;
&lt;iframe src=&quot;https://pepelsbey.dev/articles/svg-sprites/demo/fragments/id.html&quot; height=&quot;360&quot; loading=&quot;lazy&quot; title=&quot;Black contour cogwheel icon on a green background, on hover it becomes purple.&quot;&gt;&lt;/iframe&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	background-image&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;sprite.svg#link&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;:hover&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;:focus&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	background-image&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;sprite.svg#hover&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You know what? It works! Not only for background images but for content images too. Though the SVG sprite needs to be organized differently. Let’s have a look and then unpack it.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;svg&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; xmlns&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;http://www.w3.org/2000/svg&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;view&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;link&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; viewBox&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;0 0 24 24&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;path&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;		transform&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;translate(0, 0)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;		fill&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;#0c0b1d&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;		d&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;M19.43…&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;view&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;hover&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; viewBox&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;24 0 24 24&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;path&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;		transform&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;translate(24, 0)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;		fill&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;#9874d3&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;		d&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;M19.43…&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;svg&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Meet another SVG element called &lt;code&gt;&amp;lt;view&amp;gt;&lt;/code&gt;, it defines a viewport with a unique ID. When you’re linking this sprite with such ID, it’s like you’re cropping into one of the predefined viewports to see just a certain fragment of the image. That’s why they called “fragment identifiers”.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;viewBox&lt;/code&gt; attribute here works the same way as for the &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; element. The first two values define x and y viewport shifts, so the “viewport camera” in our case will make two moves to get each icon: &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;24&lt;/code&gt;. You can learn more about the &lt;code&gt;viewBox&lt;/code&gt; attribute &lt;a href=&quot;https://www.sarasoueidan.com/blog/svg-art-direction-using-viewbox/&quot;&gt;in Sara Soueidan’s article&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you look at the &lt;code&gt;d&lt;/code&gt; attribute’s starting points, they’re identical! But don’t let it fool you: there’s a &lt;code&gt;transform&lt;/code&gt; attribute right next to it, that translates those icons to the right by &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;24&lt;/code&gt;. Yes, it’s a “true sprite” where icons are sitting next to each other. But compared to the old-school method, it’s much easier to use IDs instead of moving the background/object position.&lt;/p&gt;
&lt;p&gt;Unfortunately, this solution is limited to background images and content images and there’s no way to change the icon’s color fill using external CSS like it was possible with inline SVG placeholders. Such a sprite won’t work with inline SVG either.&lt;/p&gt;
&lt;h3&gt;Alt syntax&lt;/h3&gt;
&lt;p&gt;While we’re at it, there’s another syntax that might be convenient in some cases. Previously, to make this “true sprite” work we had to mark it with &lt;code&gt;&amp;lt;view&amp;gt;&lt;/code&gt; elements and unique IDs. But we can also tell what fragment of the sprite we need right in the URL, using &lt;code&gt;svgView&lt;/code&gt; and &lt;code&gt;viewBox&lt;/code&gt; parameters.&lt;/p&gt;
&lt;iframe src=&quot;https://pepelsbey.dev/articles/svg-sprites/demo/fragments/view.html&quot; height=&quot;360&quot; loading=&quot;lazy&quot; title=&quot;Black contour cogwheel icon on a green background, on hover it becomes purple.&quot;&gt;&lt;/iframe&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	background-image&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;		url&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;sprite.svg#svgView(viewBox(0, 0, 24, 24))&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;:hover&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;:focus&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	background-image&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;		url&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;sprite.svg#svgView(viewBox(24, 0, 24, 24))&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This one will show the second icon on hover because of the 24 pixels shift. I know, it looks a bit ugly, but it’s going to work with any “true sprite”, even the old-school ones. And there’s no need for IDs or some extra markup, just make sure that all icons will have their place (naturally or via transform) and start moving your viewport!&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;svg&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; xmlns&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;http://www.w3.org/2000/svg&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;path&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;		transform&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;translate(0, 0)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;		fill&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;#0c0b1d&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;		d&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;M19.43…&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;path&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;		transform&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;translate(24, 0)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;		fill&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;#9874d3&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;		d&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;M19.43…&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;svg&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It’s yet another feature from the &lt;a href=&quot;https://www.w3.org/TR/SVG2/linking.html#ViewElement&quot;&gt;SVG specification&lt;/a&gt; that’s been forgotten for some reason. That’s a pity, because “Can I use” &lt;a href=&quot;https://caniuse.com/svg-fragment&quot;&gt;looks pretty good&lt;/a&gt; for fragment identifiers.&lt;/p&gt;
&lt;p&gt;But there’s a catch 😅&lt;/p&gt;
&lt;h3&gt;A catch&lt;/h3&gt;
&lt;p&gt;For some reason, browsers treat URLs with fragment identifiers as different resources. Just like in the first naive demo: the first &lt;code&gt;sprite.svg#link&lt;/code&gt; file will be loaded by default, and the second &lt;code&gt;sprite.svg#hover&lt;/code&gt; will be loaded again on hover. As two different files! Even with the &lt;code&gt;svgView()&lt;/code&gt; syntax. And it seems like it’s not just a request to the cache for the same file: if you throttle the network, you’ll see the delay. Only Safari takes the file from memory, but sometimes hover stucks.&lt;/p&gt;
&lt;p&gt;I think it’s the perfect time to file some browser bugs. This is what we all have to do when we encounter a bug in a browser. Leave the place better than you found it, right?&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Let’s see where we are with all these methods so far:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Moving old-school sprites in background/content images is probably not a good idea. Maybe for bitmap sprites only.&lt;/li&gt;
&lt;li&gt;Symbols are great for styling but don’t work for background/content images.&lt;/li&gt;
&lt;li&gt;Fragments are super convenient with sprites in background/content images, but there’s no easy way to style them and they’re buggy.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If only there was a method to combine all the symbols’ and fragments’ advantages…&lt;/p&gt;
&lt;p&gt;You know, the way I said “if only” and the next chapter that’s coming up implies that there’s a solution for that. You got me! 🥸 There’s one: not ideal, but pretty close. And it’s not even new, it’s just forgotten.&lt;/p&gt;
&lt;h2&gt;Forgotten stacks&lt;/h2&gt;
&lt;p&gt;Before diving into yet another SVG spriting method, let’s answer the most important question: does CSS styling work? Yes, it does. That’s what we’re going to try first.&lt;/p&gt;
&lt;iframe src=&quot;https://pepelsbey.dev/articles/svg-sprites/demo/stacks/inline.html&quot; height=&quot;360&quot; loading=&quot;lazy&quot; title=&quot;Black contour cogwheel icon on a green background, on hover it becomes purple.&quot;&gt;&lt;/iframe&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; aria-label&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;Settings&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;svg&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; aria-hidden&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; width&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;200&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; height&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;200&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;sprite.svg#settings&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;svg&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Tell me if you’ve seen this one before: the inline SVG placeholder inherits the CSS styling and passes it into the sprite. But the real magic is happening behind the curtain and it’s called SVG stacks. One of the first mentions it got was Simurai’s “&lt;a href=&quot;https://simurai.com/blog/2012/04/02/svg-stacks&quot;&gt;SVG Stacks&lt;/a&gt;” blog post from 2012 where they together with Erik Dahlström figured out a way to use good old &lt;code&gt;:target&lt;/code&gt; pseudo-class for that.&lt;/p&gt;
&lt;p&gt;Let’s pull the curtain and see what our &lt;em&gt;sprite.svg&lt;/em&gt; is made of:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;svg&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; xmlns&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;http://www.w3.org/2000/svg&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;defs&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;style&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;			:root svg:not(:target) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;				display: none;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;			}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;style&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;defs&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;svg&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;settings&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; viewBox&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;0 0 24 24&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; fill&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;currentcolor&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; d&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;M19.43…&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;svg&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;svg&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;favorite&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; viewBox&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;0 0 24 24&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; fill&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;currentcolor&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; d&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;M16.5…&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;svg&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;svg&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Just like &lt;code&gt;&amp;lt;symbol&amp;gt;&lt;/code&gt;, our icons don’t get their place since they’re stacked on top of each other. Hence the name, I guess. But they’re not hidden by default, unlike &lt;code&gt;&amp;lt;symbol&amp;gt;&lt;/code&gt;. That’s why we hide them with &lt;code&gt;display: none&lt;/code&gt; but not all of them, only the ones that aren’t targeted by ID in the sprite’s URL.&lt;/p&gt;
&lt;p&gt;As for the &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; wrappers for each icon, they serve an important role in making all that beautiful auto-scaling thanks to the &lt;code&gt;viewBox&lt;/code&gt; attribute. That’s also why there’s a complicated &lt;code&gt;:root svg&lt;/code&gt; selector: it says “affect only nested &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt; elements”, which makes sense since there’s a parent one too.&lt;/p&gt;
&lt;p&gt;But the most exciting part is that it also works for &lt;a href=&quot;https://pepelsbey.dev/articles/svg-sprites/demo/stacks/background.html&quot;&gt;background images&lt;/a&gt; and &lt;a href=&quot;https://pepelsbey.dev/articles/svg-sprites/demo/stacks/img.html&quot;&gt;content images&lt;/a&gt;.&lt;/p&gt;
&lt;iframe src=&quot;https://pepelsbey.dev/articles/svg-sprites/demo/stacks/background.html&quot; height=&quot;360&quot; loading=&quot;lazy&quot; title=&quot;Black contour cogwheel icon on a green background, it does not become purple on hover.&quot;&gt;&lt;/iframe&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;a&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	background-image&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;url&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&#39;sprite.svg#settings&#39;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I’m sorry for your frustration if you’ve just tried to hover it. Unfortunately, it only works for placing images. This CSS styling inheritance thing doesn’t work because there’s no SVG placeholder. It’s just an image linked from the same file. But it gives us a choice: we can use the same sprite for all applications and when we need to change the icon’s color fill, we’ll make sure to use the SVG placeholder.&lt;/p&gt;
&lt;p&gt;But if you really want this kind of sprite to work, it’s possible to create multiple instances of the same icon with different colors and IDs via &lt;code&gt;&amp;lt;use&amp;gt;&lt;/code&gt; and change ID in CSS on hover. But this is a story for another article 😉&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;svg&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; xmlns&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;http://www.w3.org/2000/svg&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;defs&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;style&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;			:root svg:not(:target) {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;				display: none;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;			}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;style&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;settings&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; d&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;M19.43…&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;defs&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;svg&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;settings-black&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; viewBox&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;0 0 24 24&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; fill&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;black&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;#settings&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;svg&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;svg&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;settings-white&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; viewBox&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;0 0 24 24&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;use&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; fill&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;white&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; href&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;#settings&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;svg&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;svg&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You might call this method a hack and this is probably fair. But it’s so basic that full browser compatibility for it goes back to 2015 or even earlier. Though I noticed behavior in Firefox that might require some fixing, but only for inline SVG placeholders.&lt;/p&gt;
&lt;h3&gt;Firefix&lt;/h3&gt;
&lt;p&gt;You see, in HTML and CSS everything is a rectangular block unless you specifically try to round it or clip it some other clever way. But in SVG everything gets a unique shape and hover behavior based exactly on its shape. For some reason, inline SVG placeholders with SVG symbol libraries keep the hover area rectangular too.&lt;/p&gt;
&lt;p&gt;But only in the case of SVG stack and only in Firefox the icon’s hover area in HTML is based on the linked SVG element’s shape, which is not ideal: your cursor falls into the icon’s holes as you move it and the whole thing starts blinking. There’s a pretty simple solution that some icon systems (like &lt;a href=&quot;https://fonts.google.com/icons&quot;&gt;Material Symbols&lt;/a&gt;) use anyway, but for a different reason.&lt;/p&gt;
&lt;p&gt;We need to put some opaque rectangles in each icon to give it a desirable hover area. They could be circles too, but rectangles would be more universal. That would be pretty easy to automate based on the icon’s &lt;code&gt;viewBox&lt;/code&gt; attribute, in case you’d like to build such a sprite based on a folder of icons.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;svg&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; id&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;settings&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; viewBox&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;0 0 24 24&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;rect&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; width&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;24&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; height&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;24&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; fill-opacity&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;0&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;path&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; fill&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;currentcolor&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; d&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;M19.43…&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;svg&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I’ll make sure to file another issue in &lt;a href=&quot;https://bugzilla.mozilla.org/&quot;&gt;Firefox’s bug tracker&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;One sprite to rule them all?&lt;/h2&gt;
&lt;p&gt;SVG stacks might finally help us not to clutter our markup with SVG placeholders (when we don’t need to style the icons from CSS) while keeping icons conveniently organized in a single file. You can now use the same sprite any way you want: for background images, for content images, or with SVG placeholders. This kind of flexibility will give you just enough complexity right when you need it.&lt;/p&gt;
&lt;p&gt;Oh, and read &lt;a href=&quot;https://www.w3.org/TR/SVG2/&quot;&gt;the SVG spec&lt;/a&gt;, it’s full of treasures 😍&lt;/p&gt;
			</content></entry><entry><title>6+5 ways to make a two-column layout: from pretty reasonable to com&amp;shy;pletely wrong</title><link href="https://pepelsbey.dev/articles/two-columns/"/><updated>2022-09-26T00:00:00Z</updated><id>https://pepelsbey.dev/articles/two-columns/</id><content type="html">&lt;p&gt;Imagine you need to create a two-column layout. Yes, the simplest one: a column on the left, a column on the right, and some gap in-between. There’s an obvious modern solution for that:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;.columns&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	display&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;grid&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	grid-template-columns&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;fr&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;fr&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	gap&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;20&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Done! Sure, but what if we need to support some older browsers? Flexbox then. All right! And what about text flowing from one column to another? No problem, multi-columns. How about old email clients? Well, some of us still remember how to use table layouts.&lt;/p&gt;
&lt;p&gt;You see, that’s the beauty of CSS: there are multiple solutions for almost every problem, so you can choose the one that fits your exact needs. But not just CSS, there are many HTML and SVG tricks that can help you in some cases. It’s like a natural language: the bigger your vocabulary is, the better you can express yourself.&lt;/p&gt;
&lt;p&gt;There’s even an interview strategy based on that: you can ask talent to come up with multiple ways of solving the same simple task. And this is exactly where the idea of this article came from.&lt;/p&gt;
&lt;p&gt;A friend of mine challenged me once with a task from the job interview: how many ways of making a two-column layout do you know? What a silly question, right? But it got me deeper than I thought. I couldn’t think of anything else for a while until I went through all the possible and &lt;em&gt;impossible&lt;/em&gt; ideas in my head. It boiled down to 11 ways of making two columns with a gap.&lt;/p&gt;
&lt;p&gt;But I’d like to call them 6+5 to split them into two groups:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Six pretty reasonable ones, that make sense and could be used in a real production project (or used to).&lt;/li&gt;
&lt;li&gt;Five completely wrong ones, that have some quirks, look or behave weirdly, but still accomplish the task.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;By the way, the results look the same in all modern browsers, even the five weird ones.&lt;/p&gt;
&lt;h2&gt;Setup and rules&lt;/h2&gt;
&lt;p&gt;To make it closer to reality, I decided to split the whole thing into two components:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Columns: fixed layout with two columns and a gap.&lt;/li&gt;
&lt;li&gt;News: fluid cards that would fit the columns.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The idea is to have a columns component that could be filled with the real content, not just to draw two colored boxes next to each other.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pepelsbey.dev/articles/two-columns/images/setup.png&quot; alt=&quot;Green and peach news cards with a title and some text sitting in a row on a violet background with a gap between them.&quot; /&gt;
The look that we’re aiming for&lt;/p&gt;
&lt;p&gt;The news component will always stay the same, we’re going to play with the columns component only. The first news will have a &lt;code&gt;lightgreen&lt;/code&gt; background, the second one — the famous &lt;code&gt;peachpuff&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Reasonable six&lt;/h2&gt;
&lt;p&gt;How would you sort the list of reasonable options? Well, probably not alphabetically. From the best to the worst? They’re all good at certain situations and have some unique advantages. So I decided to go with the historical order: I’ll start with the ones that I’ve learned first and finish with the modern ones.&lt;/p&gt;
&lt;h3&gt;Tables&lt;/h3&gt;
&lt;p&gt;⚙️ &lt;a href=&quot;https://pepelsbey.dev/articles/two-columns/demo/table.html&quot;&gt;Demo: two columns and a gap with tables&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Tables were the first layout tool available in browsers. And I used them to create my first webpage back in 2002. To make a table layout you need a parent wrapper &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt;, some &lt;code&gt;&amp;lt;tr&amp;gt;&lt;/code&gt; rows, and &lt;code&gt;&amp;lt;td&amp;gt;&lt;/code&gt; cells for columns.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;table&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;columns&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;tr&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;td&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;columns__item columns__item--first&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#6A737D;--shiki-light:#6A737D&quot;&gt;		&amp;#x3C;!-- Left --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;td&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;td&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;columns__item columns__item--second&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#6A737D;--shiki-light:#6A737D&quot;&gt;		&amp;#x3C;!-- Right --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;td&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;tr&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;table&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I’m going to use BEM notation for class names, just like I’d do in a real project. And we’re going to use pretty much the same column component structure for all demos, but in some cases, we won’t need first/second modifiers.&lt;/p&gt;
&lt;p&gt;It’s worth noting that even though tables are listed in the “reasonable” group, they’re quite outdated and should be used only for… you know, tables and tabular data. You might have a reason to use them for email layouts, but I’m not even sure if they’re needed there anymore. And it’s a nightmare from the accessibility point of view, so let’s consider it a history lesson.&lt;/p&gt;
&lt;p&gt;To make tables disappear and behave like a neutral column component we need to fix some things: &lt;code&gt;border-collapse&lt;/code&gt; and &lt;code&gt;padding&lt;/code&gt; properties to remove extra padding and &lt;code&gt;vertical-align: top&lt;/code&gt; to align content to the top. Yes, tables used to be the easiest way to align things vertically.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;.columns&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	border-collapse&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;collapse&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;.columns__item&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	padding&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	width&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;50&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	vertical-align&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;top&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To make a gap in 2002 I’d use another empty cell in the middle with some extra element to fix the width. Wild times! But today I’d prefer some padding instead: 10px from the left and 10px from the right, nothing too fancy.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;.columns__item--first&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	padding-right&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;10&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;.columns__item--second&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	padding-left&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;10&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You might think that using &lt;code&gt;display: table&lt;/code&gt; on a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; could be considered another way of making a two-column layout. But I think that tables are tables, doesn’t matter if this behavior comes from browser or author styles.&lt;/p&gt;
&lt;p&gt;And here comes the news:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;article&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;news&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;h2&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;news__title&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;Title&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;h2&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;news__lead&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;Content&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;article&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once we have both news in each table cell, the first “reasonable” layout is ready. Ten more to go!&lt;/p&gt;
&lt;h3&gt;Floats&lt;/h3&gt;
&lt;p&gt;⚙️ &lt;a href=&quot;https://pepelsbey.dev/articles/two-columns/demo/float.html&quot;&gt;Demo: two columns and a gap with floats&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The next layout technique I learned were floats. They were invented for a newspaper or magazine-like content layouts where text would “float” around pictures, quotes, or similar elements. I tried this first in Adobe PageMaker when laying out an actual newspaper and it was very nice to have floats available on the Web too.&lt;/p&gt;
&lt;p&gt;Some clever people realized that if you’d get rid of text and float one box to the left and another to the right, that would make a layout! Though it’s important to make sure that floated elements won’t compete for space, otherwise they’d just start dropping down from the row.&lt;/p&gt;
&lt;p&gt;In this case, we won’t need any special HTML elements to make it work, so let’s stick with abstract divs. It’s just a layout, after all.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;columns&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;columns__item columns__item--first&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#6A737D;--shiki-light:#6A737D&quot;&gt;		&amp;#x3C;!-- Left --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;columns__item columns__item--second&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#6A737D;--shiki-light:#6A737D&quot;&gt;		&amp;#x3C;!-- Right --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;div&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here comes the main catch with floats: they need to be “cleared”. If you have floating elements in your container, they will fall out of it and the container would collapse to zero height.&lt;/p&gt;
&lt;p&gt;There are two main ways of clearing floats:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Change some properties of the container.&lt;/li&gt;
&lt;li&gt;Put some fake content at the end of the container.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let’s go with the first option. Back in float layouts days, we’d use &lt;code&gt;overflow: hidden&lt;/code&gt;, which comes with the obvious drawback: content gets clipped. But today we can use a special &lt;code&gt;display&lt;/code&gt; value:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;.columns&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	display&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;flow-root&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I’d call it &lt;code&gt;display: clear-floats&lt;/code&gt; instead, but that’s why I don’t have a chance to get into &lt;a href=&quot;https://wiki.csswg.org/&quot;&gt;CSSWG&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Now we need to set up columns’ width and since they’re not glued together like table cells, it’s possible to set them apart with half of the width minus half of the gap. The magic of &lt;code&gt;calc&lt;/code&gt; wasn’t available back then, just like &lt;code&gt;border-radius&lt;/code&gt;, but it’s 2022, so:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;.columns__item&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	width&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;calc&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;50&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt; -&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; 10&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s finally float them to different sides of the parent:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;.columns__item--first&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	float&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;left&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;.columns__item--second&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	float&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;right&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And there you have it! The second slightly more “reasonable” two-column option. Let’s try the next one!&lt;/p&gt;
&lt;h3&gt;Inline blocks&lt;/h3&gt;
&lt;p&gt;⚙️ &lt;a href=&quot;https://pepelsbey.dev/articles/two-columns/demo/inline-block.html&quot;&gt;Demo: two columns and a gap with inline blocks&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Layouts based on inline blocks were popular around the same time as floats. But they were a little bit more finicky to deal with. We’ll use the same markup as with floats, but we won’t need any first/second modifiers.&lt;/p&gt;
&lt;p&gt;First of all, we need to make inline blocks out of our columns to make the whole thing work. Since they are inline they’re happy to stay “in line”, but they’re also blocks and you can still set their width (unlike just inline elements). Let’s also align them to the top, not the default baseline.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;.columns__item&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	display&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;inline-block&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	width&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;calc&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;50&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt; -&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; 10&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	vertical-align&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;top&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://pepelsbey.dev/articles/two-columns/images/inline.png&quot; alt=&quot;Green and peach news cards with a tiny gap between them.&quot; /&gt;
A gap that doesn’t look right&lt;/p&gt;
&lt;p&gt;Now our news blocks are in “columns”, but the gap between them doesn’t look right. It looks like a typical white space. Well, because it is! All the nesting in our HTML is routinely squashed by the browser into a single white space since it’s an inline context.&lt;/p&gt;
&lt;p&gt;There are two popular ways to get rid of it:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Set the parent’s font size to zero.&lt;/li&gt;
&lt;li&gt;Remove all the spaces between the tags in markup.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The second way is rather fragile, so let’s go with the first one. And since &lt;code&gt;font-size&lt;/code&gt; is an inherited property, let’s not forget to revert it for the content.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;.columns&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	font-size&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;.columns__item&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	font-size&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;16&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once we have both our columns sitting right next to each other, we can make the exact 20px gap between them. Since it’s the inline context, we can treat our parent element as a sentence, which makes nested columns words… do you see where it’s going? That’s right! The &lt;code&gt;word-spacing&lt;/code&gt; property will do the trick.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;.columns&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	word-spacing&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;20&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	font-size&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;.columns__item&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	word-spacing&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;normal&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	font-size&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;16&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let’s not forget to reset it to &lt;code&gt;normal&lt;/code&gt; for the nested elements, just like we did for the &lt;code&gt;font-size&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;That was the third way, the next three will finally start making sense, I promise.&lt;/p&gt;
&lt;h3&gt;Multi-columns&lt;/h3&gt;
&lt;p&gt;⚙️ &lt;a href=&quot;https://pepelsbey.dev/articles/two-columns/demo/multi-column.html&quot;&gt;Demo: two columns and a gap with multi-columns&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It’s time for the first layout technique that was designed for layouts. Well, almost. Multi-columns can take any content and make it flow through the columns with some native gaps in-between. As seen in newspapers!&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;.columns&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	columns&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;2&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; 20&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That’s it! I’m not a big fan of magic shorthand properties like &lt;code&gt;flex&lt;/code&gt;, but I just couldn’t resist. Two columns and a 20px gap set in a single property! Isn’t it elegant? But there’s something wrong:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pepelsbey.dev/articles/two-columns/images/multi.png&quot; alt=&quot;Green and peach news cards in a row, but the second card’s heading starts in the first column and the rest goes to the second.&quot; /&gt;
Broken TV effect&lt;/p&gt;
&lt;p&gt;Since content is flowing from one column to another, some block parts are flowing too. It looks like a broken portal or an old TV, but there’s an easy fix: a polite &lt;code&gt;avoid&lt;/code&gt; value for the brutal &lt;code&gt;break-inside&lt;/code&gt; property.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;.columns__item&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	break-inside&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;avoid&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That was quick! The fourth two-column layout. Let’s see if there’s anything even better than that.&lt;/p&gt;
&lt;h3&gt;Flexbox&lt;/h3&gt;
&lt;p&gt;⚙️ &lt;a href=&quot;https://pepelsbey.dev/articles/two-columns/demo/flexbox.html&quot;&gt;Demo: two columns and a gap with Flexbox&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Here comes the most popular layout technique these days. It’s been around for a while, but back in the old days there used to be differences in browser implementations and just &lt;a href=&quot;https://github.com/philipwalton/flexbugs&quot;&gt;obvious bugs&lt;/a&gt; that made Flexbox tricky to use. But not anymore!&lt;/p&gt;
&lt;p&gt;Now it’s as easy as:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;.columns&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	display&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;flex&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	gap&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;20&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;.columns__item&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	width&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;50&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But if you don’t have the luxury of supporting only recent browser versions, you’ll have to say goodbye to the &lt;code&gt;gap&lt;/code&gt; property and use some extra code to make some space between columns. Push the columns to the sides and make sure their width is set with &lt;code&gt;calc&lt;/code&gt; just like we did before.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;.columns&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	display&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;flex&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	justify-content&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;space-between&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;.columns__item&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	width&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;calc&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;50&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt; -&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; 10&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, something modern and usable, the fifth already! Flexbox is relevant today, unlike many techniques we’ve discussed. But these days I often reach for the next option.&lt;/p&gt;
&lt;h3&gt;Grid Layout&lt;/h3&gt;
&lt;p&gt;⚙️ &lt;a href=&quot;https://pepelsbey.dev/articles/two-columns/demo/table.html&quot;&gt;Demo: two columns and a gap with Grid Layout&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Seriously, Grid Layout makes so much sense in almost every layout situation, even for micro-layouts like putting an icon next to a word. Remember? This is what we’ve started from:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;.columns&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	display&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;grid&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	grid-template-columns&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;1&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;fr&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; 1&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;fr&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	gap&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;20&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The beauty of it is that the whole layout is defined by the container. Sure, in some cases you’ll need to apply some properties to the nested elements, but it’s possible to achieve basic layouts using just the container’s properties. It’s especially useful for making your layouts responsive with Media Queries.&lt;/p&gt;
&lt;p&gt;Also, because &lt;code&gt;grid-gap&lt;/code&gt; and later just &lt;code&gt;gap&lt;/code&gt; properties were part of the initial Grid Layout implementations, you don’t have to worry about browser compatibility so much, compared to &lt;code&gt;gap&lt;/code&gt; in Flexbox.&lt;/p&gt;
&lt;p&gt;That was &lt;em&gt;way too simple&lt;/em&gt; the sixth way of making a two-column layout. Don’t worry, we have some pretty weird things coming up.&lt;/p&gt;
&lt;h2&gt;Weird five&lt;/h2&gt;
&lt;p&gt;There’s no historical order here. I just tried to list the options from the least weird to completely wrong. And what were the problems that made me split these methods into a special group?&lt;/p&gt;
&lt;p&gt;First of all, they’re not always playing nice with the content flow. On the Web, we used a principle that the next content block would go right after the previous one, not on top of it. And once the previous block gets smaller or bigger, all the following blocks move up or down with it.&lt;/p&gt;
&lt;p&gt;If you ever hand-coded an SVG file, you probably know what I’m talking about. Imagine if every block would be absolutely positioned at the top left corner of the document. That would make our job much more difficult. It’s totally fine for the SVG as an image format, but not acceptable for a content layout.&lt;/p&gt;
&lt;p&gt;Other methods are making things too complicated with extra markup, misusing some CSS properties, making it work only in a single browser, or compromising the content accessibility. Still, let’s explore them one by one to learn something new, or at least have some fun.&lt;/p&gt;
&lt;h3&gt;Positioning&lt;/h3&gt;
&lt;p&gt;⚙️ &lt;a href=&quot;https://pepelsbey.dev/articles/two-columns/demo/position.html&quot;&gt;Demo: two columns and a gap with positioning&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Positioning is not the best layout technique because it breaks the content flow, one of the main principles of the Web. But it’s still a useful tool in some cases. Unlike shapes in SVG, we don’t have to position elements from the top left corner of the document every time: fortunately, there’s a way to nest positioning.&lt;/p&gt;
&lt;p&gt;Let’s keep the parent component in the flow with &lt;code&gt;position: relative&lt;/code&gt;. In this case, nested column positioning will start from the parent component, even though it will collapse to zero height just like with floats. Unfortunately, there’s no way to “clear” positioned elements.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;.columns&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	position&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;relative&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;.columns__item&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	position&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;absolute&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	top&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	width&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;calc&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;50&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt; -&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; 10&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since absolutely positioned elements are in their &lt;em&gt;parallel world,&lt;/em&gt; they tend to contain things in a funny way, so let’s limit their width with &lt;code&gt;calc&lt;/code&gt;. And just like with floats, let’s push our columns to the sides so they won’t overlap.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;.columns__item--first&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	left&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;.columns__item--second&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	right&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;0&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://pepelsbey.dev/articles/two-columns/images/position.png&quot; alt=&quot;Green and peach news cards in a row, but on a tomato background this time.&quot; /&gt;
The first dangerously red option&lt;/p&gt;
&lt;p&gt;Hmm, there’s something different with this demo! Unlike previous purple demos, this one has a page background filled with tomato color. That’s because it looks slightly more dangerous to highlight the nature of this group.&lt;/p&gt;
&lt;p&gt;So there you have it: the first weird way. Nothing too scary, right? Of course, we’re just warming up.&lt;/p&gt;
&lt;h3&gt;Writing mode&lt;/h3&gt;
&lt;p&gt;⚙️ &lt;a href=&quot;https://pepelsbey.dev/articles/two-columns/demo/writing-mode.html&quot;&gt;Demo: two columns and a gap with writing mode&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;To understand how the next method works, let’s think about this very text: not the meaning of it, but the shape. I’m writing it in horizontal lines that go one after another from top to bottom. This behavior is common for many languages and controlled with the &lt;code&gt;writing-mode&lt;/code&gt; property. In this case, its value is &lt;code&gt;horizontal-tb&lt;/code&gt;, meaning “horizontal, top to bottom”.&lt;/p&gt;
&lt;p&gt;But in some languages text could go in vertical columns, not horizontal rows. This gives us two other &lt;code&gt;writing-mode&lt;/code&gt; values: &lt;code&gt;vertical-rl&lt;/code&gt; and &lt;code&gt;vertical-lr&lt;/code&gt;. The first part of the value is fairly simple, the second depends on the direction of the text: LTR or RTL. Anyway, new lines in this vertical mode go either to the left or to the right from the previous one.&lt;/p&gt;
&lt;p&gt;Knowing that let’s try a silly thing: change the parent’s block writing mode to vertical, so the lines would become columns and start from the right.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;.columns&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	writing-mode&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;vertical-lr&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://pepelsbey.dev/articles/two-columns/images/writing.png&quot; alt=&quot;Green and peach news cards in a row on a tomato background, but peach goes first, there’s no gap, and each card is rotated 90 degrees clockwise.&quot; /&gt;
You might have to tilt your head a bit&lt;/p&gt;
&lt;p&gt;See, this already looks like a layout! But some things need to be fixed to make it usable. Just like in &lt;code&gt;font-size: 0&lt;/code&gt; case we need to restore the &lt;code&gt;writing-mode&lt;/code&gt; for the columns to the previous state. And while we’re at it, let’s add width to our columns.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;.columns__item&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	width&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;390&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	writing-mode&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;horizontal-tb&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Unfortunately, there’s no way for us to use the &lt;code&gt;gap&lt;/code&gt; property outside of Flexbox or Grid Layout. So let’s use the good old trick: a column followed by another column will get the right margin.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;.columns__item&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt; +&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; .columns__item&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	margin-left&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;20&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I probably should’ve used &lt;code&gt;.columns__item—first&lt;/code&gt; selector instead, but that would be way too easy. I’m trying to use as many tricks as possible here!&lt;/p&gt;
&lt;p&gt;Hopefully, you can smell the same weird thing in both &lt;code&gt;font-size: 0&lt;/code&gt; and &lt;code&gt;writing-mode: vertical-lr&lt;/code&gt; cases: they both fragile and misuse properties that weren’t meant for layout.&lt;/p&gt;
&lt;p&gt;Still, the second weird two-column layout. Ready for another one? Let’s go!&lt;/p&gt;
&lt;h3&gt;SVG&lt;/h3&gt;
&lt;p&gt;⚙️ &lt;a href=&quot;https://pepelsbey.dev/articles/two-columns/demo/svg.html&quot;&gt;Demo: two columns and a gap with SVG&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I already mentioned SVG as merely a graphics format that could be hand-coded, but doesn’t fit our layout needs. Sorry, but I lied to you. You weren’t ready for the truth at the beginning. But now you’ve been through a lot of weird stuff and are ready for anything.&lt;/p&gt;
&lt;p&gt;Let’s start from CSS… and finish right away. This is the only styling we’re going to need.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;.columns&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	display&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;block&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	width&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;100&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	height&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;100&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can already see that this method is as friendly to content flow as absolute positioning (not at all). As for HTML, it’s not going to look pretty:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;svg&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;columns&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;foreignObject&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;article&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;news news--first&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;			&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;h2&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;news__title&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;Title&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;h2&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;			&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;news__lead&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;Content&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;article&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;foreignObject&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;foreignObject&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;article&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;news news--second&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;			&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;h2&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;news__title&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;Title&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;h2&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;			&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;news__lead&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;Content&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;p&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;		&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;article&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;foreignObject&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;svg&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Well, it’s not exactly HTML, but rather SVG with some HTML inside. Still, inside of the HTML document. I don’t know if it’s legal, but it’s &lt;a href=&quot;https://validator.w3.org/nu/#textarea&quot;&gt;fully valid&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Document checking completed. No errors or warnings to show.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Usually, SVG won’t allow you to have some arbitrary HTML inside, apart from similarly named &lt;a href=&quot;https://www.w3.org/TR/SVG2/linking.html#AElement&quot;&gt;&lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://www.w3.org/TR/SVG2/interact.html#ScriptElement&quot;&gt;&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;&lt;/a&gt; SVG elements. But if you ask nicely using &lt;code&gt;&amp;lt;foreignObject&amp;gt;&lt;/code&gt; it’ll be fine.&lt;/p&gt;
&lt;p&gt;To make it work, we need to position these foreign agents… sorry, I mean foreign objects using presentational attributes. This is pretty common and quite handy in SVG since it’s merely a graphics format, remember? Instead of &lt;code&gt;left/top&lt;/code&gt; we have &lt;code&gt;x/y&lt;/code&gt;, the rest is pretty similar. But there’s no easy way to make &lt;code&gt;right: 0&lt;/code&gt; alternative, so we’ll have to position the right column from the left as well.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;foreignObject&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; x&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;0&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; y&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;0&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; width&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;390&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; height&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;100%&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#6A737D;--shiki-light:#6A737D&quot;&gt;	&amp;#x3C;!-- Left --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;foreignObject&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;foreignObject&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; x&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;410&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; y&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;0&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; width&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;390&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; height&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;100%&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#6A737D;--shiki-light:#6A737D&quot;&gt;	&amp;#x3C;!-- Right --&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;foreignObject&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Unfortunately, there’s no way for SVG-wrapped content to influence the parent’s dimensions as HTML elements do. So we’ll have to set it ourselves: in our case, it takes the whole page’s height.&lt;/p&gt;
&lt;p&gt;That’s the third weird two-column layout. Let’s explore a slightly more reasonable fourth one to prepare for the worst.&lt;/p&gt;
&lt;h3&gt;Element&lt;/h3&gt;
&lt;p&gt;⚙️ &lt;a href=&quot;https://pepelsbey.dev/articles/two-columns/demo/element.html&quot;&gt;Demo: two columns and a gap with element&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;When setting up the rules, I mentioned that we’re trying to make something practical here, not just draw two boxes next to each other. But there is a way to take some real content and draw it as a background image. It’s not Canvas, it only works in Firefox, and you should never use it. Sounds exciting!&lt;/p&gt;
&lt;p&gt;To make it work, let’s resize our columns to half of the parent width minus half of the gap, the usual thing. Then we clip them so they would become invisible and take them out of the flow with positioning. Sure, why not.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;.columns__item&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	position&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;absolute&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	clip-path&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;inset&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;50&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	width&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;calc&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;50&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt; -&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; 10&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;px&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://pepelsbey.dev/articles/two-columns/images/element.png&quot; alt=&quot;DevTool’s overlay box with the class name and dimensions over an invisible news card on a tomato background.&quot; /&gt;
Not &lt;code&gt;display: none&lt;/code&gt;, but visually hidden&lt;/p&gt;
&lt;p&gt;See, the columns are still there, but they’re invisible. Let’s put them back the way we need them! But the parent’s height is collapsed now without any content, let’s fix it with &lt;code&gt;height: 100%&lt;/code&gt;. Relative positioning would keep those columns sizing and position relative to the parent block.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;.columns&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	position&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;relative&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	height&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;100&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now it’s time for some magic. Only for this demo we have IDs for each news in our markup: &lt;code&gt;news-first&lt;/code&gt; and &lt;code&gt;news-second&lt;/code&gt;. We can use those IDs to make these elements sources for &lt;code&gt;background-image&lt;/code&gt; property with &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/element&quot;&gt;&lt;code&gt;-moz-element&lt;/code&gt;&lt;/a&gt; function. Thanks to multiple background images, we can use just a single element for that. Positioning our elements: the first goes to &lt;code&gt;left top&lt;/code&gt;, the second goes to &lt;code&gt;right top&lt;/code&gt;. And we don’t need the repeating.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;.columns&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	background-image&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;		-moz-element&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(#news-first),&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;		-moz-element&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;(#&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;news-second&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	background-position&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;		left&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; top&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;		right&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt; top&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	background-repeat&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;no-repeat&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The CSS syntax of VS Code thinks that something is wrong with IDs in the functions, but it works! Well, only in Firefox at the moment. And as I mentioned before, it’s not ready to be used in any production code. Though it’s not just made up, since it’s a part of &lt;a href=&quot;https://drafts.csswg.org/css-images-4/#element-notation&quot;&gt;CSS Images Module Level 4 draft&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Let’s hope this feature will be supported in all browsers at some point. It’s been around for a while only in Firefox. But once again, using it for laying out content is not a good idea in any case.&lt;/p&gt;
&lt;p&gt;The fourth weird layout method wasn’t that bad compared to what’s coming next. I sincerely apologize in advance.&lt;/p&gt;
&lt;h3&gt;Frames&lt;/h3&gt;
&lt;p&gt;⚙️ &lt;a href=&quot;https://pepelsbey.dev/articles/two-columns/demo/frame.html&quot;&gt;Demo: two columns and a gap with frames&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You might know what &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; is, but you probably haven’t used the &lt;code&gt;&amp;lt;frame&amp;gt;&lt;/code&gt; element much. It serves a similar purpose by giving you a “window” to another document. The main difference between them is that &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; is a standalone element, but &lt;code&gt;&amp;lt;frame&amp;gt;&lt;/code&gt; elements come in sets called &lt;code&gt;&amp;lt;frameset&amp;gt;&lt;/code&gt;. And those framesets have some layout capabilities!&lt;/p&gt;
&lt;p&gt;To make the layout that we’re aiming for, we’ll need three frames in a set: two for columns and one in the middle for the gap. The exact widths for our frames could be specified in the &lt;code&gt;cols&lt;/code&gt; attribute. It doesn’t matter that the total width exceeds 100%, browsers won’t overflow the set, just like they do with tables.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;frameset&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; cols&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;50%, 20, 50%&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt; border&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;0&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;frame&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; frameborder&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;0&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; src&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;frame&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; frameborder&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;0&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; src&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;frame&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; frameborder&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;0&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; src&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;frameset&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Unlike the &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; elements where “i” stands for “inline”, &lt;code&gt;&amp;lt;frameset&amp;gt;&lt;/code&gt; is supposed to take the whole window. Not only that, it is supposed to replace the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; element. It would be impossible to use such a layout technique on a page with other elements. No problem! We can wrap it in another inline frame.&lt;/p&gt;
&lt;p&gt;Frames also require external documents to work, so you’d have to separate your &lt;code&gt;&amp;lt;frameset&amp;gt;&lt;/code&gt; into &lt;em&gt;columns.html&lt;/em&gt; and link it in &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; via &lt;code&gt;src&lt;/code&gt; attribute. You’d also need to separate news into &lt;em&gt;news-one.html&lt;/em&gt; and &lt;em&gt;news-two.html&lt;/em&gt; files and link them via &lt;code&gt;src&lt;/code&gt; attributes as well. Remember, I apologized for the method in advance!&lt;/p&gt;
&lt;p&gt;But there’s another way we can make it work without external files and nested documents. Well, sort of. We can use &lt;code&gt;data:uri&lt;/code&gt; and nest everything in a single document. But we should be careful with quotes, you’ll see why.&lt;/p&gt;
&lt;p&gt;Let’s start with CSS for the &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt;, nothing too fancy:&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt;.columns&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	display&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;block&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	width&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;100&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	height&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;100&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#F97583;--shiki-light:#D73A49&quot;&gt;%&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;	border&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;: &lt;/span&gt;&lt;span style=&quot;--shiki-dark:#79B8FF;--shiki-light:#005CC5&quot;&gt;none&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And here comes the markup, the most exciting part. Instead of the URL of the file in &lt;code&gt;src&lt;/code&gt; attribute, we have its content with the special &lt;code&gt;data:text/html,&lt;/code&gt; prefix to let the browser know that it’s not a URL, but the “file” itself. The content starts with &lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/code&gt; to stay in &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Quirks_Mode_and_Standards_Mode&quot;&gt;standards mode&lt;/a&gt;, then follows the charset (just in case). I skipped the &lt;code&gt;&amp;lt;title&amp;gt;&lt;/code&gt; element because I’m a bad person. Please don’t ever do it.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;iframe&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;columns&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; src&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;data:text/html,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;!DOCTYPE html&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;meta charset=&#39;utf-8&#39;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;frameset cols=&#39;50%,20,50%&#39; border=&#39;0&#39;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;		&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;frame frameborder=&#39;0&#39; src=&#39;data:text/html,&#39;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;		&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;frame frameborder=&#39;0&#39; src=&#39;data:text/html,&#39;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;		&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;frame frameborder=&#39;0&#39; src=&#39;data:text/html,&#39;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;/frameset&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;iframe&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we have three nested frames with empty files in &lt;code&gt;src&lt;/code&gt; attributes. We’re going to keep the middle one empty because it’s just a gap. As for the other two, there will be our news documents. I usually have double quotes in my markup, but I had to switch to single ones in the nested document to make it work. On the next nesting level, I’ll just stop using them altogether.&lt;/p&gt;
&lt;p&gt;So let’s get the actual content the same way we did with the &lt;code&gt;&amp;lt;frameset&amp;gt;&lt;/code&gt;: barebones HTML document, some styles, and the news. Unfortunately, I couldn’t make the &lt;code&gt;&amp;lt;link rel=&amp;quot;stylesheet&amp;quot; href=&amp;quot;news.css&amp;quot;&amp;gt;&lt;/code&gt; work, so I had to use inline styles. But I wouldn’t blame it for giving up in such a mess of a markup.&lt;/p&gt;
&lt;pre class=&quot;shiki shiki-themes github-dark github-light&quot; style=&quot;--shiki-dark:#e1e4e8;--shiki-light:#24292e;--shiki-dark-bg:#24292e;--shiki-light-bg:#fff&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;iframe&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; class&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;columns&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#B392F0;--shiki-light:#6F42C1&quot;&gt; src&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;data:text/html,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;!DOCTYPE html&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;meta charset=&#39;utf-8&#39;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;frameset cols=&#39;50%, 20, 50%&#39; border=&#39;0&#39;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;		&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;frame frameborder=&#39;0&#39; src=&#39;data:text/html,&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;			&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;!DOCTYPE html&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;			&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;meta charset=utf-8&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;			&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;style&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;				/* News styles */&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;			&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;/style&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;			&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;article class=news&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;				&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;h2 class=news__title&gt;Title&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;/h2&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;				&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;p class=news__lead&gt;Content&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;/p&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;			&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;/article&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;		&#39;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic;--shiki-light:#B31D28;--shiki-light-font-style:italic&quot;&gt;	&amp;#x3C;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;/frameset&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;line&quot;&gt;&lt;span style=&quot;--shiki-dark:#9ECBFF;--shiki-light:#032F62&quot;&gt;&quot;&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&amp;#x3C;/&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#85E89D;--shiki-light:#22863A&quot;&gt;iframe&lt;/span&gt;&lt;span style=&quot;--shiki-dark:#E1E4E8;--shiki-light:#24292E&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The same goes for the second news, the only difference is background color and content. And the thing that surprises me the most is that it works in Firefox, Chrome, and Safari, even though &lt;code&gt;&amp;lt;frameset&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;frame&amp;gt;&lt;/code&gt; elements are deprecated for a long time.&lt;/p&gt;
&lt;p&gt;The only problem I couldn’t solve is the &lt;code&gt;&amp;lt;frameset&amp;gt;&lt;/code&gt; background color in Safari: for some reason, it’s white, though it’s transparent in other browsers. This behavior is not mentioned anywhere, &lt;a href=&quot;https://html.spec.whatwg.org/multipage/rendering.html#frames-and-framesets&quot;&gt;even in HTML spec&lt;/a&gt; that describes &lt;code&gt;&amp;lt;frame&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;frameset&amp;gt;&lt;/code&gt; behavior in detail for compatibility reasons.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pepelsbey.dev/articles/two-columns/images/frames.png&quot; alt=&quot;Green and peach news cards in a row on a tomato background, but the gap and the space below them are filled with white.&quot; /&gt;
Safari ruined an otherwise &lt;em&gt;perfectly viable&lt;/em&gt; layout option&lt;/p&gt;
&lt;p&gt;That was the last weird two-column technique I came up with. Was it practical? Hell no! Did I have a lot of fun building it? Definitely.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;I hope you’ve learned something new along the way. That’s the beauty of the Web platform: there are multiple ways of doing the same thing. And if you know all of them you’re practically unstoppable and ready to implement anything that life might challenge you with.&lt;/p&gt;
&lt;p&gt;And if you liked this kind of thinking, you might enjoy the “&lt;a href=&quot;https://youtu.be/TUeb4MBbhTE&quot;&gt;How do I draw a line?&lt;/a&gt;” video, by Heydon Pickering.&lt;/p&gt;
			</content></entry><entry><title>A third breath</title><link href="https://pepelsbey.dev/articles/a-third-breath/"/><updated>2022-09-26T00:00:00Z</updated><id>https://pepelsbey.dev/articles/a-third-breath/</id><content type="html">&lt;p&gt;On February 5th, 2008 I published a post called “&lt;a href=&quot;https://pepelsbey.net/2008/02/second-breath/&quot;&gt;A second breath&lt;/a&gt;” on my old blog. Unfortunately, it’s in Russian, so you might not have a chance to enjoy it. This post’s first comment suggested using jQuery to make rounded corners. &lt;a href=&quot;https://jquery.malsup.com/corner/&quot;&gt;And it wasn’t a joke&lt;/a&gt; 😳&lt;/p&gt;
&lt;p&gt;Anyway, as you might’ve guessed by the title, it wasn’t my first attempt at blogging. It lasted for a while until I stopped posting in 2014. You know, too busy with social media, conferences, podcasting, and other stuff.&lt;/p&gt;
&lt;p&gt;Eight years later I’m taking the third breath and starting another blog. Lucky for you, this time it’s in English. Lucky for me, I’m not promising to post daily as I did in 2008.&lt;/p&gt;
&lt;p&gt;I’m not starting with just a promise, you can already read a few articles: new “&lt;a href=&quot;https://pepelsbey.dev/articles/two-columns/&quot;&gt;6+5 ways to make a two-column layout&lt;/a&gt;” and old “&lt;a href=&quot;https://www.smashingmagazine.com/2019/02/buttons-interfaces/&quot;&gt;When is a button not a button?&lt;/a&gt;” published in Smashing Magazine in 2019. But there’s more! I already have another article in the queue and a big list of ideas. &lt;a href=&quot;https://pepelsbey.dev/feed/&quot;&gt;Subscribe to RSS&lt;/a&gt; so you won’t miss them!&lt;/p&gt;
&lt;p&gt;It’s been busy eight years, so now I have &lt;a href=&quot;https://pepelsbey.dev/projects/&quot;&gt;a few projects&lt;/a&gt; you might find interesting. And if you’re curious about who I am, you can find &lt;a href=&quot;https://pepelsbey.dev/about/&quot;&gt;some answers too&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;There are no comments this time, but you can always let me know what you think &lt;a href=&quot;https://twitter.com/pepelsbey_dev&quot;&gt;on Twitter&lt;/a&gt;. Enjoy! ✨&lt;/p&gt;
			</content></entry><entry><title>When is a button not a button?</title><link href="https://www.smashingmagazine.com/2019/02/buttons-interfaces/"/><updated>2019-02-25T00:00:00Z</updated><id>https://pepelsbey.dev/articles/not-a-button/</id><content type="html"></content></entry></feed>
