Browse Source

Links

master
David Larlet 1 month ago
parent
commit
604db80bc5
Signed by: David Larlet <david@larlet.fr> GPG Key ID: 3E2953A359E7E7BD

+ 177
- 0
cache/2024/0e3d54128711421c0878723dafed66e8/index.html
File diff suppressed because it is too large
View File


+ 10
- 0
cache/2024/0e3d54128711421c0878723dafed66e8/index.md
File diff suppressed because it is too large
View File


+ 533
- 0
cache/2024/4e116948ed4d26daa981a6c4ea9e4282/index.html View File

@@ -0,0 +1,533 @@
<!doctype html><!-- This is a valid HTML5 document. -->
<!-- Screen readers, SEO, extensions and so on. -->
<html lang="en">
<!-- Has to be within the first 1024 bytes, hence before the `title` element
See: https://www.w3.org/TR/2012/CR-html5-20121217/document-metadata.html#charset -->
<meta charset="utf-8">
<!-- Why no `X-UA-Compatible` meta: https://stackoverflow.com/a/6771584 -->
<!-- The viewport meta is quite crowded and we are responsible for that.
See: https://codepen.io/tigt/post/meta-viewport-for-2015 -->
<meta name="viewport" content="width=device-width,initial-scale=1">
<!-- Required to make a valid HTML5 document. -->
<title>Write CSS. Not too much. Mostly scoped. (archive) — David Larlet</title>
<meta name="description" content="Publication mise en cache pour en conserver une trace.">
<!-- That good ol' feed, subscribe :). -->
<link rel="alternate" type="application/atom+xml" title="Feed" href="/david/log/">
<!-- Generated from https://realfavicongenerator.net/ such a mess. -->
<link rel="apple-touch-icon" sizes="180x180" href="/static/david/icons2/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/static/david/icons2/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/static/david/icons2/favicon-16x16.png">
<link rel="manifest" href="/static/david/icons2/site.webmanifest">
<link rel="mask-icon" href="/static/david/icons2/safari-pinned-tab.svg" color="#07486c">
<link rel="shortcut icon" href="/static/david/icons2/favicon.ico">
<meta name="msapplication-TileColor" content="#f7f7f7">
<meta name="msapplication-config" content="/static/david/icons2/browserconfig.xml">
<meta name="theme-color" content="#f7f7f7" media="(prefers-color-scheme: light)">
<meta name="theme-color" content="#272727" media="(prefers-color-scheme: dark)">
<!-- Is that even respected? Retrospectively? What a shAItshow…
https://neil-clarke.com/block-the-bots-that-feed-ai-models-by-scraping-your-website/ -->
<meta name="robots" content="noai, noimageai">
<!-- Documented, feel free to shoot an email. -->
<link rel="stylesheet" href="/static/david/css/style_2021-01-20.css">
<!-- See https://www.zachleat.com/web/comprehensive-webfonts/ for the trade-off. -->
<link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t3_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t3_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t3_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
<script>
function toggleTheme(themeName) {
document.documentElement.classList.toggle(
'forced-dark',
themeName === 'dark'
)
document.documentElement.classList.toggle(
'forced-light',
themeName === 'light'
)
}
const selectedTheme = localStorage.getItem('theme')
if (selectedTheme !== 'undefined') {
toggleTheme(selectedTheme)
}
</script>

<meta name="robots" content="noindex, nofollow">
<meta content="origin-when-cross-origin" name="referrer">
<!-- Canonical URL for SEO purposes -->
<link rel="canonical" href="https://www.leereamsnyder.com/write-css-not-too-much-mostly-scoped">

<body class="remarkdown h1-underline h2-underline h3-underline em-underscore hr-center ul-star pre-tick" data-instant-intensity="viewport-all">


<article>
<header>
<h1>Write CSS. Not too much. Mostly scoped.</h1>
</header>
<nav>
<p class="center">
<a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
</svg> Accueil</a> •
<a href="https://www.leereamsnyder.com/write-css-not-too-much-mostly-scoped" title="Lien vers le contenu original">Source originale</a>
<br>
Mis en cache le 2024-03-23
</p>
</nav>
<hr>
<details id="table-of-contents"><summary data-svelte-h="svelte-nbhsu6"><span class="sc">Table of contents</span></summary>
</details>
<p class="arbitrary-details-space svelte-1tvynej"></p>
<p>I’ve been a web developer of various stripes for over 15 years now. At roughly the same time that I was starting to sling <span class="sc">HTML</span> and <span class="sc">CSS</span> onto the internet, the author and journalist Michael Pollan attempted to answer the <a href="https://www.npr.org/2008/01/01/17725932/in-defense-of-food-author-offers-advice-for-health">“supposedly incredibly complicated and confusing question of what we humans should eat in order to be maximally healthy”</a>. He came up with some simple guidelines that could be boiled down to seven little words:</p>
<blockquote>
<p>Eat food. Not too much. Mostly plants.</p>
</blockquote>
<p>You should eat stuff that is <em>recognizably food</em> instead of hyper-processed garbage. Keep your portions reasonable. Prefer fruits and vegetables, which are overall better for you and the environment.</p>
<p>It was simple, sensible, and backed by science. With that, we never argued about what we should be putting in our bodies ever again.</p>
<p>Just kidding! Flash forward to today and we spend our time <a href="https://www.healthline.com/nutrition/atkins-vs-keto#keto">rehashing dietary trends from last century</a>, <a href="https://www.garbageday.email/p/lifehack-your-water">arguing about water</a>, <a href="https://en.wikipedia.org/wiki/Consumption_of_Tide_Pods#Tide_Pod_Challenge">urging people not to eat Tide Pods</a>, and fighting over <a href="https://www.youtube.com/watch?v=RADEfBJmtk4">appropriate vessels to contain water</a>.</p>
<p>Perhaps Pollan underestimated how much we like to argue online.</p>
<p>I see that same love of arguing playing out in web development with the endless discussion over <a href="https://tailwindcss.com">Tailwind, the utility-first <span class="sc">CSS</span> framework and toolset</a> that hit the scene in 2017.</p>
<p>If you’re somehow unfamiliar with Tailwind, the idea is the framework provides a gigantic pile of utility classes. Take some meat-and-potatoes markup like this (an abridged sample <a href="https://tailwindcss.com/docs/utility-first">from their docs</a>):</p>
<pre><code class="language-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"chat-notification"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"chat-notification-content"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">h4</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"chat-notification-title"</span>&gt;</span>ChitChat<span class="hljs-tag">&lt;/<span class="hljs-name">h4</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="language-css">
<span class="hljs-selector-class">.chat-notification</span> {
<span class="hljs-attribute">display</span>: flex;
<span class="hljs-attribute">max-width</span>: <span class="hljs-number">24rem</span>;
<span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span> auto;
<span class="hljs-attribute">padding</span>: <span class="hljs-number">1.5rem</span>;
<span class="hljs-attribute">border-radius</span>: <span class="hljs-number">0.5rem</span>;
<span class="hljs-attribute">background-color</span>: <span class="hljs-number">#fff</span>;
<span class="hljs-attribute">box-shadow</span>:
<span class="hljs-number">0</span> <span class="hljs-number">20px</span> <span class="hljs-number">25px</span> -<span class="hljs-number">5px</span> <span class="hljs-built_in">rgba</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0.1</span>),
<span class="hljs-number">0</span> <span class="hljs-number">10px</span> <span class="hljs-number">10px</span> -<span class="hljs-number">5px</span> <span class="hljs-built_in">rgba</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0.04</span>);
}

<span class="hljs-selector-class">.chat-notification-content</span> {
<span class="hljs-attribute">margin-left</span>: <span class="hljs-number">1.5rem</span>;
<span class="hljs-attribute">padding-top</span>: <span class="hljs-number">0.25rem</span>;
}

<span class="hljs-selector-class">.chat-notification-title</span> {
<span class="hljs-attribute">color</span>: <span class="hljs-number">#1a202c</span>;
<span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.25rem</span>;
<span class="hljs-attribute">line-height</span>: <span class="hljs-number">1.25</span>;
}
</span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>
</code></pre>
<p>With Tailwind, you bring in their huge stylesheet or (very preferably) use their build tooling and build the same component like so, using only the library’s classes that often apply a single styling property like font size or color or background color:</p>
<pre><code class="language-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"p-6 max-w-sm mx-auto bg-white rounded-xl shadow-lg flex items-center space-x-4"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-xl font-medium text-black"</span>&gt;</span>ChitChat<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Take a beat and examine your feelings about those code samples. Do either of them raise your hackles or make you want to yell at strangers online?</p>
<p>You might think that using Tailwind and coding in this style would be a personal preference, but it <em>clearly</em> hits something deeper. Tailwind is possibly the most divisive thing I’ve seen in my career as a developer.</p>
<p>Here’s a small list of articles. First, some <strong>pro</strong> ones:</p>

<p>And some <strong>con</strong> ones:</p>

<p>Most recently, here’s <a href="https://heydonworks.com/article/what-is-utility-first-css/">What is Utility-First CSS?</a> by Heydon Pickering, which is very spicy:</p>
<blockquote>
<p>So why is this utility-first approach so popular at the moment? Partly because the designs we’re charged with coding often are f**ked and we need equally f**ked tools to wrangle them. Partly it’s because the f**ked tools we’ve adopted to write f**ked JavaScript don’t play so well with CSS or, for that matter, HTML. Mostly, it’s because developer insecurity and neophilia are easily exploited: “Have you ever written CSS you weren’t quite happy with? Well here’s a radical, paradigm-shifting, quasi-proprietary solution! You’ll never embarrass yourself again!”</p>
<p>It turns out, people in tech are particularly bad at distinguishing between paradigm shifts and paradigm sharts. That’s why we have nose-diving cryptocurrencies, dust-collecting monkey JPEG portfolios, and AI-generated children’s books teaching kids about pink, two-headed dinosaurs that never existed.</p>
</blockquote>
<p>I mean c’mon that’s pretty funny.</p>
<p>We can’t stop talking about it. Here I am talking about it more.</p>
<p>I recommend that you read all of those articles, as both sides make plenty of interesting points. But in the interest of your time, if I had to <em>greatly</em> reduce their arguments, the <strong>Yay-Tailwind</strong> folks are saying:</p>
<ul>
<li>Tailwind’s utility-first, just-use-these-classes philosophy clicks for me in a way that the <a href="http://smacss.com">many</a>, <a href="https://getbem.com">many</a>, <a href="https://technotif.com/manage-large-css-projects-with-itcss/">many</a> mental models for managing <span class="sc">CSS</span> haven’t.</li>
<li>Managing <span class="sc">CSS</span> at scale is very hard, and I mostly don’t have to do that with Tailwind.</li>
<li>Tailwind makes it easy to put together something that looks OK quickly. (This partially confuses the usefulness of a utility-first philosophy with the usefulness of a whole-ass design system, but, sure, OK. You like it.)</li>
</ul>
<p>Meanwhile, the <strong>Down-With-Tailwind</strong> folks say:</p>
<ul>
<li>You’re adding a dependency on Tailwind’s build tooling for customization and (especially) performance. Are you really looking for more complexity in your tech stack?</li>
<li>Because Tailwind is theoretically replacing all your <span class="sc">CSS</span>, their surface area is huge. If you’re going to <a href="https://v2.tailwindcss.com/docs">learn a bunch of Tailwind</a> to do box models, margin, padding, borders, typography, sizing, spacing, box-shadows, backgrounds, colors, fonts, animations, <a href="https://tailwindcss.com/docs/backdrop-hue-rotate">backdrop hue rotations</a>(???) etc etc, why not just learn <span class="sc">CSS</span>?</li>
<li>If you are choosing to not learn <span class="sc">CSS</span>, you might regret that choice when you have to troubleshoot your Tailwind code, which is inevitably just <span class="sc">CSS</span>. (I personally don’t see this brought up often enough.)</li>
<li>The markup is ugly!</li>
</ul>
<p>So, <strong>where do I stand</strong>?</p>
<p>I have built stuff with and without Tailwind professionally, and I see the merits of both sides.</p>
<p>The way Tailwind <a href="https://dev.to/cher/sexism-racism-toxic-positivity-and-tailwindcss-cil">actively pushes against making hasty abstractions</a> is — really — the smartest thing about it. In my experience, when you’re building something new you’re better off making something functional quickly and worrying about code elegance and deduplication and abstractions later, when you’re hopefully still in business. With a little practice, in a Tailwind project it’s relatively easy to get into a just-building-the-thing flow state. I get to shove the part of me that frets about good naming and specificity and leaking styles and efficient reuse into a drawer for a bit. It’s kinda nice.</p>
<p>And, sure, the output looks decent, if you accept that I will be able to spot your Tailwind-powered dashboard from <a href="https://twitter.com/chriscoyier/status/1331303400835837953?lang=en">a mile away</a> just like the (approx.) 7 quadrillion <a href="https://getbootstrap.com/docs/5.0/getting-started/introduction/">Bootstrap</a>-powered dashboards I’ve seen. (“You can customize it!” “You didn’t, though.”)</p>
<p>On the other hand, the pleasant-in-use simplicity of Tailwind falls apart when you’re doing something of even moderate complexity:</p>
<pre><code class="language-html"><span class="hljs-tag">&lt;<span class="hljs-name">button</span>
<span class="hljs-attr">class</span>=<span class="hljs-string">"
[<span class="hljs-symbol">&amp;amp;</span>&gt;[data-slot=icon]]:-mx-0.5
[<span class="hljs-symbol">&amp;amp;</span>&gt;[data-slot=icon]]:my-0.5
[<span class="hljs-symbol">&amp;amp;</span>&gt;[data-slot=icon]]:shrink-0
[<span class="hljs-symbol">&amp;amp;</span>&gt;[data-slot=icon]]:size-5
[<span class="hljs-symbol">&amp;amp;</span>&gt;[data-slot=icon]]:sm:my-1
[<span class="hljs-symbol">&amp;amp;</span>&gt;[data-slot=icon]]:sm:size-4
[<span class="hljs-symbol">&amp;amp;</span>&gt;[data-slot=icon]]:text-[--btn-icon]
[--btn-bg:theme(colors.zinc.900)]
[--btn-border:theme(colors.zinc.950/90%)]
[--btn-hover-overlay:theme(colors.white/10%)]
[--btn-icon:theme(colors.zinc.400)]
after:-z-10
after:absolute
after:data-[active]:bg-[--btn-hover-overlay]
after:data-[disabled]:shadow-none
after:data-[hover]:bg-[--btn-hover-overlay]
after:inset-0
after:rounded-[calc(theme(borderRadius.lg)-1px)]
after:shadow-[shadow:inset_0_1px_theme(colors.white/15%)]
before:-z-10
before:absolute
before:bg-[--btn-bg]
before:data-[disabled]:shadow-none
before:inset-0
before:rounded-[calc(theme(borderRadius.lg)-1px)]
before:shadow
bg-[--btn-border]
border
border-transparent
dark:[--btn-bg:theme(colors.zinc.600)]
dark:[--btn-hover-overlay:theme(colors.white/5%)]
dark:after:-inset-px
dark:after:rounded-lg
dark:before:hidden
dark:bg-[--btn-bg]
dark:border-white/5
dark:text-white
data-[active]:[--btn-icon:theme(colors.zinc.300)]
data-[disabled]:opacity-50
data-[focus]:outline
data-[focus]:outline-2
data-[focus]:outline-blue-500
data-[focus]:outline-offset-2
data-[hover]:[--btn-icon:theme(colors.zinc.300)]
focus:outline-none
font-semibold
forced-colors:[--btn-icon:ButtonText]
forced-colors:data-[hover]:[--btn-icon:ButtonText]
gap-x-2
inline-flex
isolate
items-center
justify-center
px-[calc(theme(spacing[3.5])-1px)]
py-[calc(theme(spacing[2.5])-1px)]
relative
rounded-lg
sm:px-[calc(theme(spacing.3)-1px)]
sm:py-[calc(theme(spacing[1.5])-1px)]
sm:text-sm/6
text-base/6
text-white"</span>
&gt;</span>
Button
<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
</code></pre>
<p>That’s for <em>one</em> (1) button. The chat notification sample above didn’t make me retch, but this? 🤢</p>
<p>Ok, look, I’m not a purist about pretty code and one look at my website or wardrobe should tell you that I have questionable taste. But I would think that both sides of this debate would say that code looks <em>absolutely bananas</em>. But no, because that’s one of the buttons from the <a href="https://catalyst.tailwindui.com/">Catalyst framework</a>, built by the Tailwind team.</p>
<p>Sure, fine, buttons can be deceptively complex and Catalyst is still in development, so maybe this changes in the future. But code like this — especially interactive elements or things that need media queries — is not atypical.</p>
<p>First, Tailwind’s build tooling <a href="https://tailwindcss.com/docs/adding-custom-styles#using-arbitrary-values">lets you define new classes on the fly in <span class="sc">HTML</span></a>. This can be relatively harmless like defining a one-off margin length. Or it could be like above with, <code>sm:py-[calc(theme(spacing[1.5])-1px)]</code> where you’re involving media queries, accessing Tailwind’s theme values, then doing math to make a one-off length and OK now admit we’re just writing <span class="sc">CSS</span> but doing so very awkwardly. They’re committed to the bit, though, I’ll give them that. If you were hoping Tailwind would force your developers to stick to the design system, they won’t. (I didn’t.)</p>
<p>Second, Tailwind’s built-in solution to address the class landfill and compact your giant class heaps into something easier to consume (<a href="https://tailwindcss.com/docs/reusing-styles#extracting-classes-with-apply"><code>@apply</code></a>) is both <a href="https://tailwindcss.com/docs/reusing-styles#avoiding-premature-abstraction">clearly not recommended</a> and also a frankly weird mishmash of nonstandard <span class="sc">CSS</span> and Tailwind-ese. It reeks of “we added this to address complaints from the haterz, but only under duress.” I wouldn’t touch it.</p>
<p>So, yeah, Tailwind: it’s kinda pleasant, while occasionally being an extraordinary pain in the ass.</p>
<p>Meanwhile, in the past couple years since Tailwind rolled around, I’d say writing <span class="sc">CSS</span> is now also kinda pleasant, and while it also can occasionally be a pain in the ass, it’s far less frequent than it used to be.</p>
<p>So to tie things back to Michael Pollan, here’s where I land on the incredibly complicated and confusing question of how we humans should style documents and applications on the web at scale:</p>
<p><strong>Write CSS. Not too much. Mostly scoped.</strong></p>
<p>I’ll break that down.</p>
<p>For styling, you should strive to write code that is recognizably <span class="sc">CSS</span>.</p>
<p>You don’t need to know everything there is to know about <span class="sc">CSS</span>, but what you do learn will be portable and future-proof.</p>
<p>If you’re currently using Tailwind and like it, that’s really fine. I get it.</p>
<p>However, you might be kinda shocked at what <span class="sc">CSS</span> looks like these days.</p>
<p>For pete’s sake, <a href="https://www.joshwcomeau.com/css/center-a-div/">centering things is easy</a>. Has been for years.</p>
<p><a href="https://moderncss.dev/equal-height-elements-flexbox-vs-grid/">You have multiple ways to equalize the heights of items in a row</a>.</p>
<p><a href="https://dev.to/akhilarjun/one-line-sticky-header-using-css-5gp3">Elements that stay “stuck” to the viewport take two lines of code</a>.</p>
<p>Hell, <a href="https://rachelandrew.co.uk/archives/2023/12/19/align-content-in-block-layout/">real soon now you won’t even need grid or flexbox to center things vertically</a>.</p>
<p>Card demo authors rejoice, <a href="https://developer.mozilla.org/en-US/blog/getting-started-with-css-container-queries/">because you can respond to an element’s container, not just the viewport</a>.</p>
<p>Really, skim through all of <a href="https://moderncss.dev">these modern solutions to old problems</a>. I have personally had to work around literally all of them, so it struck a lot of nerves.</p>
<p>CSS variables, <span class="sc">AKA</span> custom properties, are universally supported. You can <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties">learn the basic syntax</a> in like 5 minutes and <a href="https://moderncss.dev/how-custom-property-values-are-computed/">learn some of the subtleties</a> in under an hour and then you’re set for life.</p>
<p>They’re great for consuming design system tokens, and with inheritance and the cascade and the ability to use them in functions and shorthands you can really cut down on the amount of new <span class="sc">CSS</span> you have to write.</p>
<pre><code class="language-css"><span class="hljs-selector-pseudo">:root</span> {
<span class="hljs-attr">--page-background-color</span>: white;
<span class="hljs-attr">--text-color</span>: black;
<span class="hljs-attr">--page-margin</span>: <span class="hljs-number">0.5rem</span>;
}

<span class="hljs-comment">/* adjust colors based on dark/light preference */</span>
<span class="hljs-keyword">@media</span> (<span class="hljs-attribute">prefers-color-scheme</span>: dark) {
<span class="hljs-selector-pseudo">:root</span> {
<span class="hljs-attr">--page-background-color</span>: black;
<span class="hljs-attr">--text-color</span>: white;
}
}

<span class="hljs-keyword">@media</span> screen <span class="hljs-keyword">and</span> (<span class="hljs-attribute">min-width</span>: <span class="hljs-number">50rem</span>) {
<span class="hljs-comment">/* increased margins on wider screens */</span>
<span class="hljs-selector-pseudo">:root</span> {
<span class="hljs-attr">--page-margin</span>: <span class="hljs-number">2rem</span>;
}
}

<span class="hljs-comment">/* use the properties once! */</span>
<span class="hljs-selector-tag">body</span> {
<span class="hljs-attribute">background-color</span>: <span class="hljs-built_in">var</span>(--page-background);
<span class="hljs-attribute">color</span>: <span class="hljs-built_in">var</span>(--text-color);
<span class="hljs-attribute">margin</span>: <span class="hljs-built_in">var</span>(--page-margin);
}
</code></pre>
<p>This is just scratching the surface. Check out <a href="https://css-irl.info/7-uses-for-css-custom-properties/">Michelle Barker’s article for more uses for custom properties</a>, <a href="https://ishadeed.com/article/practical-css-variables/">other ideas from Ahmad Shadeed</a>, or a <a href="https://lea0.verou.me/tag/css-variables/">whole pile of articles from Lea Verou</a>.</p>
<p><a href="https://css-irl.info/css-nesting-is-here/">For real</a>, you can just write this in regular <span class="sc">CSS</span> and it works.</p>
<pre><code class="language-css"><span class="hljs-selector-class">.body-copy</span> {
<span class="hljs-attribute">font-family</span>: <span class="hljs-built_in">var</span>(--font-family, serif);

<span class="hljs-comment">/*
equivalent to .body-copy &gt; * + *
*/</span>
&gt; * + * {
<span class="hljs-attribute">margin-block-start</span>: <span class="hljs-number">1em</span>;
<span class="hljs-attribute">max-width</span>: <span class="hljs-number">80ch</span>;
}
}
</code></pre>
<p>Length math methods like <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/min"><code>min()</code></a>, <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/max"><code>max()</code></a>, <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/clamp"><code>clamp()</code></a> let you implement sophisticated constraints using just <span class="sc">CSS</span>. Check out how <a href="https://utopia.fyi">Utopia</a> lets you build a fluid type and sizing scale with <code>clamp()</code> and zero media queries.</p>
<p>And, while we’re at it, it’s beyond safe to do some math in your stylesheets. The <code>calc()</code> function has been fully supported for years now.</p>
<p>Color, too, has gotten real neat in the past few years with <a href="https://chriscoyier.net/2023/06/06/modern-css-in-real-life/#new-colors">new color spaces</a> and the <code>color-mix()</code> function. <a href="https://www.abeautifulsite.net/posts/better-buttons-with-color-mix-and-custom-properties/">Check out how Cory LaViska builds a button</a> that will automatically compute reasonable hover and active colors from its current color in just a few lines of code.</p>
<p>Finally! You can say “style this, but only if it contains that” with <code>:has()</code> and <a href="https://webkit.org/blog/13096/css-has-pseudo-class/">it is glorious</a>.</p>
<p>Check out <a href="https://moderncss.dev/modern-css-for-dynamic-component-based-architecture/#component-buttons">how Stephanie Eckles builds a button</a> component that can change its skin to accommodate text, icons, or any combination by checking its own internals with <code>:has()</code>. 👨‍🍳♥️</p>
<p>I have <a href="#use-tooling-that-generates-scoped-css">one major exception</a> which I’ll get in to later.</p>
<p>But in the year two thousand and twenty four you maybe don’t need some of the classic make-<span class="sc">CSS</span>-better tooling anymore. Native support for nesting and variables probably covers most of what you’re doing with SASS or PostCSS, and browsers have moved past prefixed experimental properties, so you can drop Autoprefixer.</p>
<p>Finally, you might not need something to concatenate all your <span class="sc">CSS</span> files if you’re writing way less <span class="sc">CSS</span> in general. Speaking of…</p>
<p>People generally have problems with <span class="sc">CSS</span> when there’s a shitload of it. I like <span class="sc">CSS</span>, but I really do think the key to long-term happiness in a project is to avoid writing more of it — if you can.</p>
<p>Thankfully, new features and modern thinking about utilities and layout have you covered.</p>
<p>The best way I’ve found to avoid writing tons of <span class="sc">CSS</span> is to have a small arsenal of reusable, battle-hardened layout helpers at the ready.</p>
<p>Ideally you should not be writing more <span class="sc">CSS</span> when you have place some elements horizontally. You should instead have a utility class or a component that <em>puts things side-by-side for you</em>, and you should reach for that first.</p>
<p>The actual implementations will depend on your needs, your tooling, and probably on your design system. <a href="/row-stack-space-layout-components">I have an article about thinking about layout in rows, stacks, and spaces</a> that might illuminate things. Or look at <a href="https://every-layout.dev/"><em>Every Layout</em></a> by Heydon Pickering and Andy Bell. Or <a href="https://smolcss.dev">SmolCSS</a> by Stephanie Eckles for some other layout primitives.</p>
<p>Any system!</p>
<p>It’s very easy to rabbit-hole on this, but even a small selection of “approved” fonts, text sizes, spacing sizes, and colors will go a long way towards improving visual consistency and discouraging weird-looking one-off pages or components.</p>
<p>Slinging them around as <a href="#use-css-variables"><span class="sc">CSS</span> variables</a> makes a ton of sense to me.</p>
<pre><code class="language-css"><span class="hljs-selector-pseudo">:root</span> {
<span class="hljs-comment">/* system goes here */</span>
<span class="hljs-attr">--heading-font-family</span>: sans-serif;
<span class="hljs-attr">--heading-line-height</span>: <span class="hljs-number">1.1</span>;
<span class="hljs-attr">--heading-color</span>: green;
}

<span class="hljs-selector-tag">h1</span>,
<span class="hljs-selector-tag">h2</span>,
<span class="hljs-selector-tag">h3</span>,
<span class="hljs-selector-tag">h4</span> {
<span class="hljs-attribute">font-family</span>: <span class="hljs-built_in">var</span>(--heading-font-family);
<span class="hljs-attribute">line-height</span>: <span class="hljs-built_in">var</span>(--heading-line-height);
<span class="hljs-attribute">color</span>: <span class="hljs-built_in">var</span>(--heading-color);
}
</code></pre>
<p>Now, <em>sticking to a system</em> requires actual discipline from both designers and developers, but that’s a whole different problem.</p>
<p>Tailwind proponents do get this right, to a point: it <em>is goddamned delightful</em> to slap together a simple component or page with a few utility classes.</p>
<p>They’re undeniably handy, but don’t write one for every single <span class="sc">CSS</span> property.</p>
<p>Here’s my personal guidelines for when to write a utility class. First, <strong>is it some styling you’re writing all the time</strong>? Second, <strong>is the class name extremely obvious</strong>? If so, go for it. Otherwise, tread carefully.</p>
<p>As an example, you’re probably making components that have a wrapper element, but all that wrapper element is really doing is serving as a <code>position: relative</code> container for children.</p>
<pre><code class="language-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"some-dropdown-wrapper-with-relative-positioning"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"absolutely-positioned-dropdown"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>This is a spot where some single-property utility classes let you apply common properties quickly, spare you from having to think of a good class name for the wrapper, and the names are <span class="sc">IMO</span> obvious because they’re discrete keywords of a property:</p>
<pre><code class="language-html"><span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="language-css">
<span class="hljs-comment">/* in your global stylesheet */</span>
<span class="hljs-selector-class">.relative</span> {
<span class="hljs-attribute">position</span>: relative;
}
<span class="hljs-selector-class">.absolute</span> {
<span class="hljs-attribute">position</span>: absolute;
}
</span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"relative"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"absolute"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>I also have utilities for accessibility or layout annoyances that I end up using on every project ever, like these:</p>
<pre><code class="language-css"><span class="hljs-selector-class">.nowrap</span>,
<span class="hljs-selector-class">.no-wrap</span> <span class="hljs-comment">/* lol I can never keep this straight */</span> {
<span class="hljs-attribute">white-space</span>: nowrap;
}

<span class="hljs-selector-class">.screen-reader-only</span> {
<span class="hljs-attribute">border</span>: <span class="hljs-number">0</span>;
<span class="hljs-attribute">clip</span>: <span class="hljs-built_in">rect</span>(<span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span>);
<span class="hljs-attribute">height</span>: <span class="hljs-number">1px</span>;
<span class="hljs-attribute">margin</span>: -<span class="hljs-number">1px</span>;
<span class="hljs-attribute">overflow</span>: hidden;
<span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span>;
<span class="hljs-attribute">position</span>: absolute;
<span class="hljs-attribute">width</span>: <span class="hljs-number">1px</span>;
}
</code></pre>
<p>Or properties derived from your design system, but now names are harder:</p>
<pre><code class="language-css"><span class="hljs-selector-class">.font-primary</span> {
<span class="hljs-attribute">font-family</span>: <span class="hljs-string">'Heliotrope'</span>, Optima, sans-serif;
}
<span class="hljs-selector-class">.font-monospace</span> {
<span class="hljs-attribute">font-family</span>: <span class="hljs-string">'Mono Lisa'</span>, monospace;
}

<span class="hljs-selector-class">.fs-0</span> {
<span class="hljs-attribute">font-size</span>: <span class="hljs-built_in">var</span>(--step-<span class="hljs-number">0</span>);
}
<span class="hljs-selector-class">.fs-1</span> {
<span class="hljs-attribute">font-size</span>: <span class="hljs-built_in">var</span>(--step-<span class="hljs-number">1</span>);
}
<span class="hljs-selector-class">.fs-2</span> {
<span class="hljs-attribute">font-size</span>: <span class="hljs-built_in">var</span>(--step-<span class="hljs-number">2</span>);
}
<span class="hljs-comment">/* and so on */</span>
</code></pre>
<p>I do like to have resets like <code>.p-0</code> or <code>.m-0</code> to zero out padding and margin in a pinch, but not more than that. With layout utilities, I try to avoid thinking about padding and margin directly.</p>
<p>I get antsy writing utility classes for colors. Which color? Background? Text? Border? Outline? Are they also going to handle changes like hover, or dark mode? Should they? Are you going to account for tints and shades? Are there going to be so many that it’s hard to keep them straight? At that point, it’s easier to just write the <span class="sc">CSS</span> for whatever component I’m dealing with.</p>
<p>All this to say: keep the selection not-too-big: utility classes lose a lot of their power if you can’t keep most of the ones available to you in your noggin <span class="sc">RAM</span>.</p>
<p>But you would be stunned at how much you can build with a small handful of <a href="#build-some-layout-helpers">layout components</a> and a couple of classes that let you access bits of your design system. That’s really the sweet spot you should be aiming for.</p>
<p>When one-offs or deviations from the system arise — which they absolutely will — there are a couple more tricks to keep new <span class="sc">CSS</span> to a minimum…</p>
<p>All browsers now support the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:is"><code>:is</code></a> and <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:where"><code>:where</code></a> pseudo-classes.</p>
<p>Both are great for writing less verbose <span class="sc">CSS</span>. The classic example is combining <code>:hover</code>/<code>:focus</code> selectors into single selectors like so:</p>
<pre><code class="language-css"><span class="hljs-selector-tag">a</span> {
<span class="hljs-attribute">color</span>: purple;
}

<span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:is</span>(<span class="hljs-selector-pseudo">:hover</span>, <span class="hljs-selector-pseudo">:focus</span>) {
<span class="hljs-attribute">color</span>: blue;
}
</code></pre>
<p>The zero specificity of <code>:where()</code> is amazing for keeping the specificity of your selectors as low as possible, which makes overrides — should you need them — a lot easier. But if you’re having specificity problems, <a href="#mostly-scoped">you might want to look at scoping</a>.</p>
<p>It’s not your enemy! Learn <a href="https://web.dev/learn/css/inheritance#which_properties_are_inherited_by_default">what properties inherit</a>, and set them as high as you possibly can.</p>
<p>One classic example is font-family. If you’ve got a primary font, set that on the <code>body</code> and everything will inherit it, and you don’t write <code>font-family</code> ever again.</p>
<pre><code class="language-css"><span class="hljs-selector-tag">body</span> {
<span class="hljs-attribute">font-family</span>: sans-serif;
}

<span class="hljs-comment">/* ahem these don't inherit by default */</span>
<span class="hljs-selector-tag">button</span>,
<span class="hljs-selector-tag">input</span>,
select,
<span class="hljs-selector-tag">textarea</span> {
<span class="hljs-attribute">font</span>: inherit;
}
</code></pre>
<p>You might not have to think of more classes if you’re adding additional markup for accessibility. You can latch on to those attributes that you’re adding anyway. <a href="https://adrianroselli.com/2021/06/using-css-to-enforce-accessibility.html">Adrian Roselli has a big roundup of ideas</a>.</p>
<pre><code class="language-css"><span class="hljs-comment">/* good if you're doing this anyway */</span>
<span class="hljs-selector-tag">textarea</span><span class="hljs-selector-attr">[aria-invalid=<span class="hljs-string">'true'</span>]</span> {
<span class="hljs-attr">--border-color</span>: red;
}
</code></pre>
<p>If you’re up to speed with modern <span class="sc">CSS</span> and writing way less of it and you’re still struggling, I’m willing to bet that your issue is managing the scope or reach of your selectors.</p>
<p>Maybe your selectors are targeting more elements than you intended, or you have conflicts when you combine components. This is where you really start fighting with specificity and selector ordering and get to thinking that <span class="sc">CSS</span> is stupid as hell.</p>
<p>Scoping your <span class="sc">CSS</span> more-or-less solves all these problems for you, and modern browsers and tooling make this increasingly accessible.</p>
<p>Although I <a href="#see-if-you-can-go-without-build-tooling">mentioned above</a> that you can live without a lot of <span class="sc">CSS</span> tooling, if you can take advantage of something that automatically writes styles scoped to a component’s markup, do it.</p>
<p>Because these work by slightly altering and (typically) increasing the specificity of your selectors, there’s <em>sometimes</em> subtleties and gotchas, but these things help you sidestep so many other <span class="sc">CSS</span> authoring issues that they’re absolutely worth it: they let you write simple selectors with very low specificity, they keep your styles from “leaking” out of the component, and they’re easier to work with than all the subtleties of the Shadow <span class="sc">DOM</span>.</p>
<p>The lightest possible thing I know of is <a href="https://www.11ty.dev/docs/languages/webc/">the WebC template language</a> and <a href="https://www.11ty.dev/docs/languages/webc/#webcscoped">its <code>webc:scoped</code> mechanism</a>. The <a href="https://svelte.dev">Svelte</a> framework also has <a href="https://geoffrich.net/posts/svelte-scoped-styles/">built-in, default support for scoped styles</a>, as does <a href="https://vuejs.org/api/sfc-css-features.html">Vue</a>.</p>
<p>If you’re using React or some other web framework that forgot that styles are a thing, CSS-in-JS libraries like <a href="https://styled-components.com/"><code>styled-components</code></a> or <a href="https://emotion.sh/docs/introduction"><code>emotion</code></a> generally output scoped styles.</p>
<p>Or look into <a href="https://github.com/css-modules/css-modules?tab=readme-ov-file"><span class="sc">CSS</span> modules</a>, where you import <code>.css</code> files in your scripts. It’s not a standard, but <a href="https://github.com/css-modules/css-modules/blob/master/docs/get-started.md">lots of build pipelines and frameworks</a> let you do it.</p>
<p>What if I told you that <span class="sc">CSS</span> now has a native, globally supported way to <em>reduce</em> specificity and effectively <em>reorder</em> entire blocks of code or imported files? <a href="https://12daysofweb.dev/2022/cascade-layers/">Meet cascade layers</a>. These still scramble my brain a little bit, but theme and library authors in particular should be looking real hard at this right now. Shipped in all browsers two years ago.</p>
<p>If you’re working in web components <span class="sc">aka</span> custom elements, the Shadow <span class="sc">DOM</span> lets you write styles that are scoped to the component’s generated, isolated markup. This is a whole thing and comes <a href="https://www.matuzo.at/blog/2023/pros-and-cons-of-shadow-dom/">with a whole heap of caveats</a>, but it’s standardized and available without extra tooling. <a href="https://blog.openreplay.com/shadow-dom--the-ultimate-guide/">This introduction to the shadow <span class="sc">DOM</span> is pretty solid.</a> If you’re really interested in web components, <a href="https://thomaswilburn.github.io/wc-book/index.html">Thomas Wilburn’s book about them</a> is the best practical overview I’ve read.</p>
<p>This is Chromium-only right now, but <code>@scope</code> lets you write boundaries for selectors, either <a href="https://developer.chrome.com/docs/css-ui/at-scope#introducing_scope">in code</a> or <a href="https://developer.chrome.com/docs/css-ui/at-scope#prelude-less_scope">based on the parent of the style tag</a>. <a href="https://fullystacked.net/scope-in-css/">Ollie Williams</a> and <a href="https://keithjgrant.com/posts/2023/04/scoped-css-is-back/">Keith J. Grant</a> have good introductions here.</p>
<p>If you’re not ready for things like cascade layers or you can’t adopt additional tooling, the battle-proven mental frameworks like <a href="https://getbem.com/introduction/">BEM</a> are and were popular for a reason. You’ve gotta get everyone on board and (ugh) think of names for things, but the broad guidelines are sound: writing modular code with low specificity class selectors can definitely help you avoid big swaths of <span class="sc">CSS</span>-at-scale problems.</p>
<p><a href="https://twitter.com/aardrian/status/1164956147499053056">Maybe do your best to keep the class names shortish, though, mmm-kay?</a> 😉</p>
<p>So, yeah, I’ve been at this for a while, and this is the answer: <strong>Write <span class="sc">CSS</span>. Not too much. Mostly scoped.</strong></p>
<p>I’m sure we’ll never argue about how to manage <span class="sc">CSS</span> ever again.</p>
</article>


<hr>

<footer>
<p>
<a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
</svg> Accueil</a> •
<a href="/david/log/" title="Accès au flux RSS"><svg class="icon icon-rss2">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-rss2"></use>
</svg> Suivre</a> •
<a href="http://larlet.com" title="Go to my English profile" data-instant><svg class="icon icon-user-tie">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-user-tie"></use>
</svg> Pro</a> •
<a href="mailto:david%40larlet.fr" title="Envoyer un courriel"><svg class="icon icon-mail">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-mail"></use>
</svg> Email</a> •
<abbr class="nowrap" title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340"><svg class="icon icon-hammer2">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-hammer2"></use>
</svg> Légal</abbr>
</p>
<template id="theme-selector">
<form>
<fieldset>
<legend><svg class="icon icon-brightness-contrast">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-brightness-contrast"></use>
</svg> Thème</legend>
<label>
<input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
</label>
<label>
<input type="radio" value="dark" name="chosen-color-scheme"> Foncé
</label>
<label>
<input type="radio" value="light" name="chosen-color-scheme"> Clair
</label>
</fieldset>
</form>
</template>
</footer>
<script src="/static/david/js/instantpage-5.1.0.min.js" type="module"></script>
<script>
function loadThemeForm(templateName) {
const themeSelectorTemplate = document.querySelector(templateName)
const form = themeSelectorTemplate.content.firstElementChild
themeSelectorTemplate.replaceWith(form)

form.addEventListener('change', (e) => {
const chosenColorScheme = e.target.value
localStorage.setItem('theme', chosenColorScheme)
toggleTheme(chosenColorScheme)
})

const selectedTheme = localStorage.getItem('theme')
if (selectedTheme && selectedTheme !== 'undefined') {
form.querySelector(`[value="${selectedTheme}"]`).checked = true
}
}

const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
window.addEventListener('load', () => {
let hasDarkRules = false
for (const styleSheet of Array.from(document.styleSheets)) {
let mediaRules = []
for (const cssRule of styleSheet.cssRules) {
if (cssRule.type !== CSSRule.MEDIA_RULE) {
continue
}
// WARNING: Safari does not have/supports `conditionText`.
if (cssRule.conditionText) {
if (cssRule.conditionText !== prefersColorSchemeDark) {
continue
}
} else {
if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
continue
}
}
mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
}

// WARNING: do not try to insert a Rule to a styleSheet you are
// currently iterating on, otherwise the browser will be stuck
// in a infinite loop…
for (const mediaRule of mediaRules) {
styleSheet.insertRule(mediaRule.cssText)
hasDarkRules = true
}
}
if (hasDarkRules) {
loadThemeForm('#theme-selector')
}
})
</script>
</body>
</html>

+ 364
- 0
cache/2024/4e116948ed4d26daa981a6c4ea9e4282/index.md View File

@@ -0,0 +1,364 @@
title: Write CSS. Not too much. Mostly scoped.
url: https://www.leereamsnyder.com/write-css-not-too-much-mostly-scoped
hash_url: 4e116948ed4d26daa981a6c4ea9e4282
archive_date: 2024-03-23
og_image: https://www.leereamsnyder.com/og-image-generator/write-css-not-too-much-mostly-scoped/og-image.jpg
description: Let’s talk about Tailwind and appease no one
favicon: data:image/svg+xml,%3csvg%20xmlns='http://www.w3.org/2000/svg'%20width='64'%20height='64'%3e%3cstyle%3e%20@media%20(prefers-color-scheme:%20dark)%20{%20.lee%20{%20fill:%20%23FF4640;%20}%20.ream%20{%20fill:%20%23EC7F00;%20}%20.sny%20{%20fill:%20%23FFB258;%20}%20.der%20{%20fill:%20%23FFDFC6;%20}%20.letters%20{%20fill:%20%23373948;%20}%20}%20%3c/style%3e%3cpath%20class='lee'%20fill='%239B70FF'%20d='M0%203.066A3.066%203.066%200%200%201%203.066%200h57.868A3.066%203.066%200%200%201%2064%203.066v14.946H0V3.066Z'/%3e%3cpath%20class='ream'%20fill='%2300749D'%20d='M64%2034.108H0V15.713h64z'/%3e%3cpath%20class='sny'%20fill='%230092C5'%20d='M64%2031.808v20.695H0V31.808z'/%3e%3cpath%20class='der'%20fill='%238BCFFC'%20d='M64%2047.904v13.03A3.066%203.066%200%200%201%2060.934%2064H3.066A3.066%203.066%200%200%201%200%2060.934v-13.03h64Z'/%3e%3cg%20class='letters'%20fill='%23fff'%20fill-rule='nonzero'%3e%3cpath%20d='M13.275%2040.809c0-4.277%207.379-14.523%207.379-20.304%200-3.102-1.927-4.7-5.17-4.7-4.089%200-8.554%202.021-13.865%206.063a20.491%2020.491%200%200%201%202.726%204.277c3.807-3.055%206.016-4.559%207.849-4.559%201.081%200%201.457.517%201.457%201.363%200%203.76-6.956%2012.643-6.956%2019.458%200%204.136%202.444%206.063%207.144%206.063%205.687%200%2010.481-2.679%2016.215-9.494-1.175-.893-2.303-1.739-3.854-3.384-3.854%205.405-7.332%207.52-10.199%207.52-1.927%200-2.726-.846-2.726-2.303ZM50.286%2048c.33-1.692%201.128-3.431%201.833-5.17-.987.047-3.384.141-5.875.141-1.692%200-1.88-1.128-1.41-2.444%202.444-6.58%206.44-10.81%2010.575-10.81%201.363%200%202.491.423%203.243%201.41-.235-.047-.517-.047-.799-.047-2.538%200-3.9%201.739-3.9%203.76s1.268%203.29%203.336%203.29c3.29%200%204.935-3.431%204.935-7.332%200-3.619-1.692-6.627-5.687-6.627-4.183%200-7.52%203.055-10.763%209.87h-.423c.423-2.021.658-3.572.658-4.559%200-3.525-2.115-5.264-5.499-5.264-2.96%200-5.78%201.363-8.883%203.854.893.987%201.645%202.256%202.491%203.807%202.021-1.833%203.478-2.679%204.935-2.679%201.081%200%201.551.705%201.551%201.88%200%201.645-.94%205.311-2.726%209.776-.705%201.739-1.363%202.115-2.679%202.115-2.02%200-3.572-.141-4.324-.141-.329%201.692-1.08%203.431-1.786%205.17%202.632-.141%2018.33-.141%2021.197%200Z'/%3e%3c/g%3e%3c/svg%3e
language: en_US

<details id="table-of-contents"><summary data-svelte-h="svelte-nbhsu6"><span class="sc">Table of contents</span></summary>
</details> <p class="arbitrary-details-space svelte-1tvynej"></p> <p>I’ve been a web developer of various stripes for over 15 years now. At roughly the same time that I was starting to sling <span class="sc">HTML</span> and <span class="sc">CSS</span> onto the internet, the author and journalist Michael Pollan attempted to answer the <a href="https://www.npr.org/2008/01/01/17725932/in-defense-of-food-author-offers-advice-for-health">“supposedly incredibly complicated and confusing question of what we humans should eat in order to be maximally healthy”</a>. He came up with some simple guidelines that could be boiled down to seven little words:</p>
<blockquote>
<p>Eat food. Not too much. Mostly plants.</p>
</blockquote>
<p>You should eat stuff that is <em>recognizably food</em> instead of hyper-processed garbage. Keep your portions reasonable. Prefer fruits and vegetables, which are overall better for you and the environment.</p>
<p>It was simple, sensible, and backed by science. With that, we never argued about what we should be putting in our bodies ever again.</p>
<p>Just kidding! Flash forward to today and we spend our time <a href="https://www.healthline.com/nutrition/atkins-vs-keto#keto">rehashing dietary trends from last century</a>, <a href="https://www.garbageday.email/p/lifehack-your-water">arguing about water</a>, <a href="https://en.wikipedia.org/wiki/Consumption_of_Tide_Pods#Tide_Pod_Challenge">urging people not to eat Tide Pods</a>, and fighting over <a href="https://www.youtube.com/watch?v=RADEfBJmtk4">appropriate vessels to contain water</a>.</p>
<p>Perhaps Pollan underestimated how much we like to argue online.</p>
<p>I see that same love of arguing playing out in web development with the endless discussion over <a href="https://tailwindcss.com">Tailwind, the utility-first <span class="sc">CSS</span> framework and toolset</a> that hit the scene in 2017.</p>
<p>If you’re somehow unfamiliar with Tailwind, the idea is the framework provides a gigantic pile of utility classes. Take some meat-and-potatoes markup like this (an abridged sample <a href="https://tailwindcss.com/docs/utility-first">from their docs</a>):</p>
<pre><code class="language-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"chat-notification"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"chat-notification-content"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">h4</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"chat-notification-title"</span>&gt;</span>ChitChat<span class="hljs-tag">&lt;/<span class="hljs-name">h4</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="language-css">
<span class="hljs-selector-class">.chat-notification</span> {
<span class="hljs-attribute">display</span>: flex;
<span class="hljs-attribute">max-width</span>: <span class="hljs-number">24rem</span>;
<span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span> auto;
<span class="hljs-attribute">padding</span>: <span class="hljs-number">1.5rem</span>;
<span class="hljs-attribute">border-radius</span>: <span class="hljs-number">0.5rem</span>;
<span class="hljs-attribute">background-color</span>: <span class="hljs-number">#fff</span>;
<span class="hljs-attribute">box-shadow</span>:
<span class="hljs-number">0</span> <span class="hljs-number">20px</span> <span class="hljs-number">25px</span> -<span class="hljs-number">5px</span> <span class="hljs-built_in">rgba</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0.1</span>),
<span class="hljs-number">0</span> <span class="hljs-number">10px</span> <span class="hljs-number">10px</span> -<span class="hljs-number">5px</span> <span class="hljs-built_in">rgba</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0.04</span>);
}

<span class="hljs-selector-class">.chat-notification-content</span> {
<span class="hljs-attribute">margin-left</span>: <span class="hljs-number">1.5rem</span>;
<span class="hljs-attribute">padding-top</span>: <span class="hljs-number">0.25rem</span>;
}

<span class="hljs-selector-class">.chat-notification-title</span> {
<span class="hljs-attribute">color</span>: <span class="hljs-number">#1a202c</span>;
<span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.25rem</span>;
<span class="hljs-attribute">line-height</span>: <span class="hljs-number">1.25</span>;
}
</span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>
</code></pre>
<p>With Tailwind, you bring in their huge stylesheet or (very preferably) use their build tooling and build the same component like so, using only the library’s classes that often apply a single styling property like font size or color or background color:</p>
<pre><code class="language-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"p-6 max-w-sm mx-auto bg-white rounded-xl shadow-lg flex items-center space-x-4"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"text-xl font-medium text-black"</span>&gt;</span>ChitChat<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>Take a beat and examine your feelings about those code samples. Do either of them raise your hackles or make you want to yell at strangers online?</p>
<p>You might think that using Tailwind and coding in this style would be a personal preference, but it <em>clearly</em> hits something deeper. Tailwind is possibly the most divisive thing I’ve seen in my career as a developer.</p>
<p>Here’s a small list of articles. First, some <strong>pro</strong> ones:</p>

<p>And some <strong>con</strong> ones:</p>

<p>Most recently, here’s <a href="https://heydonworks.com/article/what-is-utility-first-css/">What is Utility-First CSS?</a> by Heydon Pickering, which is very spicy:</p>
<blockquote>
<p>So why is this utility-first approach so popular at the moment? Partly because the designs we’re charged with coding often are f**ked and we need equally f**ked tools to wrangle them. Partly it’s because the f**ked tools we’ve adopted to write f**ked JavaScript don’t play so well with CSS or, for that matter, HTML. Mostly, it’s because developer insecurity and neophilia are easily exploited: “Have you ever written CSS you weren’t quite happy with? Well here’s a radical, paradigm-shifting, quasi-proprietary solution! You’ll never embarrass yourself again!”</p>
<p>It turns out, people in tech are particularly bad at distinguishing between paradigm shifts and paradigm sharts. That’s why we have nose-diving cryptocurrencies, dust-collecting monkey JPEG portfolios, and AI-generated children’s books teaching kids about pink, two-headed dinosaurs that never existed.</p>
</blockquote>
<p>I mean c’mon that’s pretty funny.</p>
<p>We can’t stop talking about it. Here I am talking about it more.</p>
<p>I recommend that you read all of those articles, as both sides make plenty of interesting points. But in the interest of your time, if I had to <em>greatly</em> reduce their arguments, the <strong>Yay-Tailwind</strong> folks are saying:</p>
<ul>
<li>Tailwind’s utility-first, just-use-these-classes philosophy clicks for me in a way that the <a href="http://smacss.com">many</a>, <a href="https://getbem.com">many</a>, <a href="https://technotif.com/manage-large-css-projects-with-itcss/">many</a> mental models for managing <span class="sc">CSS</span> haven’t.</li>
<li>Managing <span class="sc">CSS</span> at scale is very hard, and I mostly don’t have to do that with Tailwind.</li>
<li>Tailwind makes it easy to put together something that looks OK quickly. (This partially confuses the usefulness of a utility-first philosophy with the usefulness of a whole-ass design system, but, sure, OK. You like it.)</li>
</ul>
<p>Meanwhile, the <strong>Down-With-Tailwind</strong> folks say:</p>
<ul>
<li>You’re adding a dependency on Tailwind’s build tooling for customization and (especially) performance. Are you really looking for more complexity in your tech stack?</li>
<li>Because Tailwind is theoretically replacing all your <span class="sc">CSS</span>, their surface area is huge. If you’re going to <a href="https://v2.tailwindcss.com/docs">learn a bunch of Tailwind</a> to do box models, margin, padding, borders, typography, sizing, spacing, box-shadows, backgrounds, colors, fonts, animations, <a href="https://tailwindcss.com/docs/backdrop-hue-rotate">backdrop hue rotations</a>(???) etc etc, why not just learn <span class="sc">CSS</span>?</li>
<li>If you are choosing to not learn <span class="sc">CSS</span>, you might regret that choice when you have to troubleshoot your Tailwind code, which is inevitably just <span class="sc">CSS</span>. (I personally don’t see this brought up often enough.)</li>
<li>The markup is ugly!</li>
</ul>
<p>So, <strong>where do I stand</strong>?</p>
<p>I have built stuff with and without Tailwind professionally, and I see the merits of both sides.</p>
<p>The way Tailwind <a href="https://dev.to/cher/sexism-racism-toxic-positivity-and-tailwindcss-cil">actively pushes against making hasty abstractions</a> is — really — the smartest thing about it. In my experience, when you’re building something new you’re better off making something functional quickly and worrying about code elegance and deduplication and abstractions later, when you’re hopefully still in business. With a little practice, in a Tailwind project it’s relatively easy to get into a just-building-the-thing flow state. I get to shove the part of me that frets about good naming and specificity and leaking styles and efficient reuse into a drawer for a bit. It’s kinda nice.</p>
<p>And, sure, the output looks decent, if you accept that I will be able to spot your Tailwind-powered dashboard from <a href="https://twitter.com/chriscoyier/status/1331303400835837953?lang=en">a mile away</a> just like the (approx.) 7 quadrillion <a href="https://getbootstrap.com/docs/5.0/getting-started/introduction/">Bootstrap</a>-powered dashboards I’ve seen. (“You can customize it!” “You didn’t, though.”)</p>
<p>On the other hand, the pleasant-in-use simplicity of Tailwind falls apart when you’re doing something of even moderate complexity:</p>
<pre><code class="language-html"><span class="hljs-tag">&lt;<span class="hljs-name">button</span>
<span class="hljs-attr">class</span>=<span class="hljs-string">"
[<span class="hljs-symbol">&amp;amp;</span>&gt;[data-slot=icon]]:-mx-0.5
[<span class="hljs-symbol">&amp;amp;</span>&gt;[data-slot=icon]]:my-0.5
[<span class="hljs-symbol">&amp;amp;</span>&gt;[data-slot=icon]]:shrink-0
[<span class="hljs-symbol">&amp;amp;</span>&gt;[data-slot=icon]]:size-5
[<span class="hljs-symbol">&amp;amp;</span>&gt;[data-slot=icon]]:sm:my-1
[<span class="hljs-symbol">&amp;amp;</span>&gt;[data-slot=icon]]:sm:size-4
[<span class="hljs-symbol">&amp;amp;</span>&gt;[data-slot=icon]]:text-[--btn-icon]
[--btn-bg:theme(colors.zinc.900)]
[--btn-border:theme(colors.zinc.950/90%)]
[--btn-hover-overlay:theme(colors.white/10%)]
[--btn-icon:theme(colors.zinc.400)]
after:-z-10
after:absolute
after:data-[active]:bg-[--btn-hover-overlay]
after:data-[disabled]:shadow-none
after:data-[hover]:bg-[--btn-hover-overlay]
after:inset-0
after:rounded-[calc(theme(borderRadius.lg)-1px)]
after:shadow-[shadow:inset_0_1px_theme(colors.white/15%)]
before:-z-10
before:absolute
before:bg-[--btn-bg]
before:data-[disabled]:shadow-none
before:inset-0
before:rounded-[calc(theme(borderRadius.lg)-1px)]
before:shadow
bg-[--btn-border]
border
border-transparent
dark:[--btn-bg:theme(colors.zinc.600)]
dark:[--btn-hover-overlay:theme(colors.white/5%)]
dark:after:-inset-px
dark:after:rounded-lg
dark:before:hidden
dark:bg-[--btn-bg]
dark:border-white/5
dark:text-white
data-[active]:[--btn-icon:theme(colors.zinc.300)]
data-[disabled]:opacity-50
data-[focus]:outline
data-[focus]:outline-2
data-[focus]:outline-blue-500
data-[focus]:outline-offset-2
data-[hover]:[--btn-icon:theme(colors.zinc.300)]
focus:outline-none
font-semibold
forced-colors:[--btn-icon:ButtonText]
forced-colors:data-[hover]:[--btn-icon:ButtonText]
gap-x-2
inline-flex
isolate
items-center
justify-center
px-[calc(theme(spacing[3.5])-1px)]
py-[calc(theme(spacing[2.5])-1px)]
relative
rounded-lg
sm:px-[calc(theme(spacing.3)-1px)]
sm:py-[calc(theme(spacing[1.5])-1px)]
sm:text-sm/6
text-base/6
text-white"</span>
&gt;</span>
Button
<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
</code></pre>
<p>That’s for <em>one</em> (1) button. The chat notification sample above didn’t make me retch, but this? 🤢</p>
<p>Ok, look, I’m not a purist about pretty code and one look at my website or wardrobe should tell you that I have questionable taste. But I would think that both sides of this debate would say that code looks <em>absolutely bananas</em>. But no, because that’s one of the buttons from the <a href="https://catalyst.tailwindui.com/">Catalyst framework</a>, built by the Tailwind team.</p>
<p>Sure, fine, buttons can be deceptively complex and Catalyst is still in development, so maybe this changes in the future. But code like this — especially interactive elements or things that need media queries — is not atypical.</p>
<p>First, Tailwind’s build tooling <a href="https://tailwindcss.com/docs/adding-custom-styles#using-arbitrary-values">lets you define new classes on the fly in <span class="sc">HTML</span></a>. This can be relatively harmless like defining a one-off margin length. Or it could be like above with, <code>sm:py-[calc(theme(spacing[1.5])-1px)]</code> where you’re involving media queries, accessing Tailwind’s theme values, then doing math to make a one-off length and OK now admit we’re just writing <span class="sc">CSS</span> but doing so very awkwardly. They’re committed to the bit, though, I’ll give them that. If you were hoping Tailwind would force your developers to stick to the design system, they won’t. (I didn’t.)</p>
<p>Second, Tailwind’s built-in solution to address the class landfill and compact your giant class heaps into something easier to consume (<a href="https://tailwindcss.com/docs/reusing-styles#extracting-classes-with-apply"><code>@apply</code></a>) is both <a href="https://tailwindcss.com/docs/reusing-styles#avoiding-premature-abstraction">clearly not recommended</a> and also a frankly weird mishmash of nonstandard <span class="sc">CSS</span> and Tailwind-ese. It reeks of “we added this to address complaints from the haterz, but only under duress.” I wouldn’t touch it.</p>
<p>So, yeah, Tailwind: it’s kinda pleasant, while occasionally being an extraordinary pain in the ass.</p>
<p>Meanwhile, in the past couple years since Tailwind rolled around, I’d say writing <span class="sc">CSS</span> is now also kinda pleasant, and while it also can occasionally be a pain in the ass, it’s far less frequent than it used to be.</p>
<p>So to tie things back to Michael Pollan, here’s where I land on the incredibly complicated and confusing question of how we humans should style documents and applications on the web at scale:</p>
<p><strong>Write CSS. Not too much. Mostly scoped.</strong></p>
<p>I’ll break that down.</p>
<p>For styling, you should strive to write code that is recognizably <span class="sc">CSS</span>.</p>
<p>You don’t need to know everything there is to know about <span class="sc">CSS</span>, but what you do learn will be portable and future-proof.</p>
<p>If you’re currently using Tailwind and like it, that’s really fine. I get it.</p>
<p>However, you might be kinda shocked at what <span class="sc">CSS</span> looks like these days.</p>
<p>For pete’s sake, <a href="https://www.joshwcomeau.com/css/center-a-div/">centering things is easy</a>. Has been for years.</p>
<p><a href="https://moderncss.dev/equal-height-elements-flexbox-vs-grid/">You have multiple ways to equalize the heights of items in a row</a>.</p>
<p><a href="https://dev.to/akhilarjun/one-line-sticky-header-using-css-5gp3">Elements that stay “stuck” to the viewport take two lines of code</a>.</p>
<p>Hell, <a href="https://rachelandrew.co.uk/archives/2023/12/19/align-content-in-block-layout/">real soon now you won’t even need grid or flexbox to center things vertically</a>.</p>
<p>Card demo authors rejoice, <a href="https://developer.mozilla.org/en-US/blog/getting-started-with-css-container-queries/">because you can respond to an element’s container, not just the viewport</a>.</p>
<p>Really, skim through all of <a href="https://moderncss.dev">these modern solutions to old problems</a>. I have personally had to work around literally all of them, so it struck a lot of nerves.</p>
<p>CSS variables, <span class="sc">AKA</span> custom properties, are universally supported. You can <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties">learn the basic syntax</a> in like 5 minutes and <a href="https://moderncss.dev/how-custom-property-values-are-computed/">learn some of the subtleties</a> in under an hour and then you’re set for life.</p>
<p>They’re great for consuming design system tokens, and with inheritance and the cascade and the ability to use them in functions and shorthands you can really cut down on the amount of new <span class="sc">CSS</span> you have to write.</p>
<pre><code class="language-css"><span class="hljs-selector-pseudo">:root</span> {
<span class="hljs-attr">--page-background-color</span>: white;
<span class="hljs-attr">--text-color</span>: black;
<span class="hljs-attr">--page-margin</span>: <span class="hljs-number">0.5rem</span>;
}

<span class="hljs-comment">/* adjust colors based on dark/light preference */</span>
<span class="hljs-keyword">@media</span> (<span class="hljs-attribute">prefers-color-scheme</span>: dark) {
<span class="hljs-selector-pseudo">:root</span> {
<span class="hljs-attr">--page-background-color</span>: black;
<span class="hljs-attr">--text-color</span>: white;
}
}

<span class="hljs-keyword">@media</span> screen <span class="hljs-keyword">and</span> (<span class="hljs-attribute">min-width</span>: <span class="hljs-number">50rem</span>) {
<span class="hljs-comment">/* increased margins on wider screens */</span>
<span class="hljs-selector-pseudo">:root</span> {
<span class="hljs-attr">--page-margin</span>: <span class="hljs-number">2rem</span>;
}
}

<span class="hljs-comment">/* use the properties once! */</span>
<span class="hljs-selector-tag">body</span> {
<span class="hljs-attribute">background-color</span>: <span class="hljs-built_in">var</span>(--page-background);
<span class="hljs-attribute">color</span>: <span class="hljs-built_in">var</span>(--text-color);
<span class="hljs-attribute">margin</span>: <span class="hljs-built_in">var</span>(--page-margin);
}
</code></pre>
<p>This is just scratching the surface. Check out <a href="https://css-irl.info/7-uses-for-css-custom-properties/">Michelle Barker’s article for more uses for custom properties</a>, <a href="https://ishadeed.com/article/practical-css-variables/">other ideas from Ahmad Shadeed</a>, or a <a href="https://lea0.verou.me/tag/css-variables/">whole pile of articles from Lea Verou</a>.</p>
<p><a href="https://css-irl.info/css-nesting-is-here/">For real</a>, you can just write this in regular <span class="sc">CSS</span> and it works.</p>
<pre><code class="language-css"><span class="hljs-selector-class">.body-copy</span> {
<span class="hljs-attribute">font-family</span>: <span class="hljs-built_in">var</span>(--font-family, serif);

<span class="hljs-comment">/*
equivalent to .body-copy &gt; * + *
*/</span>
&gt; * + * {
<span class="hljs-attribute">margin-block-start</span>: <span class="hljs-number">1em</span>;
<span class="hljs-attribute">max-width</span>: <span class="hljs-number">80ch</span>;
}
}
</code></pre>
<p>Length math methods like <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/min"><code>min()</code></a>, <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/max"><code>max()</code></a>, <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/clamp"><code>clamp()</code></a> let you implement sophisticated constraints using just <span class="sc">CSS</span>. Check out how <a href="https://utopia.fyi">Utopia</a> lets you build a fluid type and sizing scale with <code>clamp()</code> and zero media queries.</p>
<p>And, while we’re at it, it’s beyond safe to do some math in your stylesheets. The <code>calc()</code> function has been fully supported for years now.</p>
<p>Color, too, has gotten real neat in the past few years with <a href="https://chriscoyier.net/2023/06/06/modern-css-in-real-life/#new-colors">new color spaces</a> and the <code>color-mix()</code> function. <a href="https://www.abeautifulsite.net/posts/better-buttons-with-color-mix-and-custom-properties/">Check out how Cory LaViska builds a button</a> that will automatically compute reasonable hover and active colors from its current color in just a few lines of code.</p>
<p>Finally! You can say “style this, but only if it contains that” with <code>:has()</code> and <a href="https://webkit.org/blog/13096/css-has-pseudo-class/">it is glorious</a>.</p>
<p>Check out <a href="https://moderncss.dev/modern-css-for-dynamic-component-based-architecture/#component-buttons">how Stephanie Eckles builds a button</a> component that can change its skin to accommodate text, icons, or any combination by checking its own internals with <code>:has()</code>. 👨‍🍳♥️</p>
<p>I have <a href="#use-tooling-that-generates-scoped-css">one major exception</a> which I’ll get in to later.</p>
<p>But in the year two thousand and twenty four you maybe don’t need some of the classic make-<span class="sc">CSS</span>-better tooling anymore. Native support for nesting and variables probably covers most of what you’re doing with SASS or PostCSS, and browsers have moved past prefixed experimental properties, so you can drop Autoprefixer.</p>
<p>Finally, you might not need something to concatenate all your <span class="sc">CSS</span> files if you’re writing way less <span class="sc">CSS</span> in general. Speaking of…</p>
<p>People generally have problems with <span class="sc">CSS</span> when there’s a shitload of it. I like <span class="sc">CSS</span>, but I really do think the key to long-term happiness in a project is to avoid writing more of it — if you can.</p>
<p>Thankfully, new features and modern thinking about utilities and layout have you covered.</p>
<p>The best way I’ve found to avoid writing tons of <span class="sc">CSS</span> is to have a small arsenal of reusable, battle-hardened layout helpers at the ready.</p>
<p>Ideally you should not be writing more <span class="sc">CSS</span> when you have place some elements horizontally. You should instead have a utility class or a component that <em>puts things side-by-side for you</em>, and you should reach for that first.</p>
<p>The actual implementations will depend on your needs, your tooling, and probably on your design system. <a href="/row-stack-space-layout-components">I have an article about thinking about layout in rows, stacks, and spaces</a> that might illuminate things. Or look at <a href="https://every-layout.dev/"><em>Every Layout</em></a> by Heydon Pickering and Andy Bell. Or <a href="https://smolcss.dev">SmolCSS</a> by Stephanie Eckles for some other layout primitives.</p>
<p>Any system!</p>
<p>It’s very easy to rabbit-hole on this, but even a small selection of “approved” fonts, text sizes, spacing sizes, and colors will go a long way towards improving visual consistency and discouraging weird-looking one-off pages or components.</p>
<p>Slinging them around as <a href="#use-css-variables"><span class="sc">CSS</span> variables</a> makes a ton of sense to me.</p>
<pre><code class="language-css"><span class="hljs-selector-pseudo">:root</span> {
<span class="hljs-comment">/* system goes here */</span>
<span class="hljs-attr">--heading-font-family</span>: sans-serif;
<span class="hljs-attr">--heading-line-height</span>: <span class="hljs-number">1.1</span>;
<span class="hljs-attr">--heading-color</span>: green;
}

<span class="hljs-selector-tag">h1</span>,
<span class="hljs-selector-tag">h2</span>,
<span class="hljs-selector-tag">h3</span>,
<span class="hljs-selector-tag">h4</span> {
<span class="hljs-attribute">font-family</span>: <span class="hljs-built_in">var</span>(--heading-font-family);
<span class="hljs-attribute">line-height</span>: <span class="hljs-built_in">var</span>(--heading-line-height);
<span class="hljs-attribute">color</span>: <span class="hljs-built_in">var</span>(--heading-color);
}
</code></pre>
<p>Now, <em>sticking to a system</em> requires actual discipline from both designers and developers, but that’s a whole different problem.</p>
<p>Tailwind proponents do get this right, to a point: it <em>is goddamned delightful</em> to slap together a simple component or page with a few utility classes.</p>
<p>They’re undeniably handy, but don’t write one for every single <span class="sc">CSS</span> property.</p>
<p>Here’s my personal guidelines for when to write a utility class. First, <strong>is it some styling you’re writing all the time</strong>? Second, <strong>is the class name extremely obvious</strong>? If so, go for it. Otherwise, tread carefully.</p>
<p>As an example, you’re probably making components that have a wrapper element, but all that wrapper element is really doing is serving as a <code>position: relative</code> container for children.</p>
<pre><code class="language-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"some-dropdown-wrapper-with-relative-positioning"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"absolutely-positioned-dropdown"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>This is a spot where some single-property utility classes let you apply common properties quickly, spare you from having to think of a good class name for the wrapper, and the names are <span class="sc">IMO</span> obvious because they’re discrete keywords of a property:</p>
<pre><code class="language-html"><span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="language-css">
<span class="hljs-comment">/* in your global stylesheet */</span>
<span class="hljs-selector-class">.relative</span> {
<span class="hljs-attribute">position</span>: relative;
}
<span class="hljs-selector-class">.absolute</span> {
<span class="hljs-attribute">position</span>: absolute;
}
</span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"relative"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"absolute"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>I also have utilities for accessibility or layout annoyances that I end up using on every project ever, like these:</p>
<pre><code class="language-css"><span class="hljs-selector-class">.nowrap</span>,
<span class="hljs-selector-class">.no-wrap</span> <span class="hljs-comment">/* lol I can never keep this straight */</span> {
<span class="hljs-attribute">white-space</span>: nowrap;
}

<span class="hljs-selector-class">.screen-reader-only</span> {
<span class="hljs-attribute">border</span>: <span class="hljs-number">0</span>;
<span class="hljs-attribute">clip</span>: <span class="hljs-built_in">rect</span>(<span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span> <span class="hljs-number">0</span>);
<span class="hljs-attribute">height</span>: <span class="hljs-number">1px</span>;
<span class="hljs-attribute">margin</span>: -<span class="hljs-number">1px</span>;
<span class="hljs-attribute">overflow</span>: hidden;
<span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span>;
<span class="hljs-attribute">position</span>: absolute;
<span class="hljs-attribute">width</span>: <span class="hljs-number">1px</span>;
}
</code></pre>
<p>Or properties derived from your design system, but now names are harder:</p>
<pre><code class="language-css"><span class="hljs-selector-class">.font-primary</span> {
<span class="hljs-attribute">font-family</span>: <span class="hljs-string">'Heliotrope'</span>, Optima, sans-serif;
}
<span class="hljs-selector-class">.font-monospace</span> {
<span class="hljs-attribute">font-family</span>: <span class="hljs-string">'Mono Lisa'</span>, monospace;
}

<span class="hljs-selector-class">.fs-0</span> {
<span class="hljs-attribute">font-size</span>: <span class="hljs-built_in">var</span>(--step-<span class="hljs-number">0</span>);
}
<span class="hljs-selector-class">.fs-1</span> {
<span class="hljs-attribute">font-size</span>: <span class="hljs-built_in">var</span>(--step-<span class="hljs-number">1</span>);
}
<span class="hljs-selector-class">.fs-2</span> {
<span class="hljs-attribute">font-size</span>: <span class="hljs-built_in">var</span>(--step-<span class="hljs-number">2</span>);
}
<span class="hljs-comment">/* and so on */</span>
</code></pre>
<p>I do like to have resets like <code>.p-0</code> or <code>.m-0</code> to zero out padding and margin in a pinch, but not more than that. With layout utilities, I try to avoid thinking about padding and margin directly.</p>
<p>I get antsy writing utility classes for colors. Which color? Background? Text? Border? Outline? Are they also going to handle changes like hover, or dark mode? Should they? Are you going to account for tints and shades? Are there going to be so many that it’s hard to keep them straight? At that point, it’s easier to just write the <span class="sc">CSS</span> for whatever component I’m dealing with.</p>
<p>All this to say: keep the selection not-too-big: utility classes lose a lot of their power if you can’t keep most of the ones available to you in your noggin <span class="sc">RAM</span>.</p>
<p>But you would be stunned at how much you can build with a small handful of <a href="#build-some-layout-helpers">layout components</a> and a couple of classes that let you access bits of your design system. That’s really the sweet spot you should be aiming for.</p>
<p>When one-offs or deviations from the system arise — which they absolutely will — there are a couple more tricks to keep new <span class="sc">CSS</span> to a minimum…</p>
<p>All browsers now support the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:is"><code>:is</code></a> and <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:where"><code>:where</code></a> pseudo-classes.</p>
<p>Both are great for writing less verbose <span class="sc">CSS</span>. The classic example is combining <code>:hover</code>/<code>:focus</code> selectors into single selectors like so:</p>
<pre><code class="language-css"><span class="hljs-selector-tag">a</span> {
<span class="hljs-attribute">color</span>: purple;
}

<span class="hljs-selector-tag">a</span><span class="hljs-selector-pseudo">:is</span>(<span class="hljs-selector-pseudo">:hover</span>, <span class="hljs-selector-pseudo">:focus</span>) {
<span class="hljs-attribute">color</span>: blue;
}
</code></pre>
<p>The zero specificity of <code>:where()</code> is amazing for keeping the specificity of your selectors as low as possible, which makes overrides — should you need them — a lot easier. But if you’re having specificity problems, <a href="#mostly-scoped">you might want to look at scoping</a>.</p>
<p>It’s not your enemy! Learn <a href="https://web.dev/learn/css/inheritance#which_properties_are_inherited_by_default">what properties inherit</a>, and set them as high as you possibly can.</p>
<p>One classic example is font-family. If you’ve got a primary font, set that on the <code>body</code> and everything will inherit it, and you don’t write <code>font-family</code> ever again.</p>
<pre><code class="language-css"><span class="hljs-selector-tag">body</span> {
<span class="hljs-attribute">font-family</span>: sans-serif;
}

<span class="hljs-comment">/* ahem these don't inherit by default */</span>
<span class="hljs-selector-tag">button</span>,
<span class="hljs-selector-tag">input</span>,
select,
<span class="hljs-selector-tag">textarea</span> {
<span class="hljs-attribute">font</span>: inherit;
}
</code></pre>
<p>You might not have to think of more classes if you’re adding additional markup for accessibility. You can latch on to those attributes that you’re adding anyway. <a href="https://adrianroselli.com/2021/06/using-css-to-enforce-accessibility.html">Adrian Roselli has a big roundup of ideas</a>.</p>
<pre><code class="language-css"><span class="hljs-comment">/* good if you're doing this anyway */</span>
<span class="hljs-selector-tag">textarea</span><span class="hljs-selector-attr">[aria-invalid=<span class="hljs-string">'true'</span>]</span> {
<span class="hljs-attr">--border-color</span>: red;
}
</code></pre>
<p>If you’re up to speed with modern <span class="sc">CSS</span> and writing way less of it and you’re still struggling, I’m willing to bet that your issue is managing the scope or reach of your selectors.</p>
<p>Maybe your selectors are targeting more elements than you intended, or you have conflicts when you combine components. This is where you really start fighting with specificity and selector ordering and get to thinking that <span class="sc">CSS</span> is stupid as hell.</p>
<p>Scoping your <span class="sc">CSS</span> more-or-less solves all these problems for you, and modern browsers and tooling make this increasingly accessible.</p>
<p>Although I <a href="#see-if-you-can-go-without-build-tooling">mentioned above</a> that you can live without a lot of <span class="sc">CSS</span> tooling, if you can take advantage of something that automatically writes styles scoped to a component’s markup, do it.</p>
<p>Because these work by slightly altering and (typically) increasing the specificity of your selectors, there’s <em>sometimes</em> subtleties and gotchas, but these things help you sidestep so many other <span class="sc">CSS</span> authoring issues that they’re absolutely worth it: they let you write simple selectors with very low specificity, they keep your styles from “leaking” out of the component, and they’re easier to work with than all the subtleties of the Shadow <span class="sc">DOM</span>.</p>
<p>The lightest possible thing I know of is <a href="https://www.11ty.dev/docs/languages/webc/">the WebC template language</a> and <a href="https://www.11ty.dev/docs/languages/webc/#webcscoped">its <code>webc:scoped</code> mechanism</a>. The <a href="https://svelte.dev">Svelte</a> framework also has <a href="https://geoffrich.net/posts/svelte-scoped-styles/">built-in, default support for scoped styles</a>, as does <a href="https://vuejs.org/api/sfc-css-features.html">Vue</a>.</p>
<p>If you’re using React or some other web framework that forgot that styles are a thing, CSS-in-JS libraries like <a href="https://styled-components.com/"><code>styled-components</code></a> or <a href="https://emotion.sh/docs/introduction"><code>emotion</code></a> generally output scoped styles.</p>
<p>Or look into <a href="https://github.com/css-modules/css-modules?tab=readme-ov-file"><span class="sc">CSS</span> modules</a>, where you import <code>.css</code> files in your scripts. It’s not a standard, but <a href="https://github.com/css-modules/css-modules/blob/master/docs/get-started.md">lots of build pipelines and frameworks</a> let you do it.</p>
<p>What if I told you that <span class="sc">CSS</span> now has a native, globally supported way to <em>reduce</em> specificity and effectively <em>reorder</em> entire blocks of code or imported files? <a href="https://12daysofweb.dev/2022/cascade-layers/">Meet cascade layers</a>. These still scramble my brain a little bit, but theme and library authors in particular should be looking real hard at this right now. Shipped in all browsers two years ago.</p>
<p>If you’re working in web components <span class="sc">aka</span> custom elements, the Shadow <span class="sc">DOM</span> lets you write styles that are scoped to the component’s generated, isolated markup. This is a whole thing and comes <a href="https://www.matuzo.at/blog/2023/pros-and-cons-of-shadow-dom/">with a whole heap of caveats</a>, but it’s standardized and available without extra tooling. <a href="https://blog.openreplay.com/shadow-dom--the-ultimate-guide/">This introduction to the shadow <span class="sc">DOM</span> is pretty solid.</a> If you’re really interested in web components, <a href="https://thomaswilburn.github.io/wc-book/index.html">Thomas Wilburn’s book about them</a> is the best practical overview I’ve read.</p>
<p>This is Chromium-only right now, but <code>@scope</code> lets you write boundaries for selectors, either <a href="https://developer.chrome.com/docs/css-ui/at-scope#introducing_scope">in code</a> or <a href="https://developer.chrome.com/docs/css-ui/at-scope#prelude-less_scope">based on the parent of the style tag</a>. <a href="https://fullystacked.net/scope-in-css/">Ollie Williams</a> and <a href="https://keithjgrant.com/posts/2023/04/scoped-css-is-back/">Keith J. Grant</a> have good introductions here.</p>
<p>If you’re not ready for things like cascade layers or you can’t adopt additional tooling, the battle-proven mental frameworks like <a href="https://getbem.com/introduction/">BEM</a> are and were popular for a reason. You’ve gotta get everyone on board and (ugh) think of names for things, but the broad guidelines are sound: writing modular code with low specificity class selectors can definitely help you avoid big swaths of <span class="sc">CSS</span>-at-scale problems.</p>
<p><a href="https://twitter.com/aardrian/status/1164956147499053056">Maybe do your best to keep the class names shortish, though, mmm-kay?</a> 😉</p>
<p>So, yeah, I’ve been at this for a while, and this is the answer: <strong>Write <span class="sc">CSS</span>. Not too much. Mostly scoped.</strong></p>
<p>I’m sure we’ll never argue about how to manage <span class="sc">CSS</span> ever again.</p>

+ 272
- 0
cache/2024/d58c7619eec894c5c069244114ea1df5/index.html View File

@@ -0,0 +1,272 @@
<!doctype html><!-- This is a valid HTML5 document. -->
<!-- Screen readers, SEO, extensions and so on. -->
<html lang="en">
<!-- Has to be within the first 1024 bytes, hence before the `title` element
See: https://www.w3.org/TR/2012/CR-html5-20121217/document-metadata.html#charset -->
<meta charset="utf-8">
<!-- Why no `X-UA-Compatible` meta: https://stackoverflow.com/a/6771584 -->
<!-- The viewport meta is quite crowded and we are responsible for that.
See: https://codepen.io/tigt/post/meta-viewport-for-2015 -->
<meta name="viewport" content="width=device-width,initial-scale=1">
<!-- Required to make a valid HTML5 document. -->
<title>Redis Renamed to Redict (archive) — David Larlet</title>
<meta name="description" content="Publication mise en cache pour en conserver une trace.">
<!-- That good ol' feed, subscribe :). -->
<link rel="alternate" type="application/atom+xml" title="Feed" href="/david/log/">
<!-- Generated from https://realfavicongenerator.net/ such a mess. -->
<link rel="apple-touch-icon" sizes="180x180" href="/static/david/icons2/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/static/david/icons2/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/static/david/icons2/favicon-16x16.png">
<link rel="manifest" href="/static/david/icons2/site.webmanifest">
<link rel="mask-icon" href="/static/david/icons2/safari-pinned-tab.svg" color="#07486c">
<link rel="shortcut icon" href="/static/david/icons2/favicon.ico">
<meta name="msapplication-TileColor" content="#f7f7f7">
<meta name="msapplication-config" content="/static/david/icons2/browserconfig.xml">
<meta name="theme-color" content="#f7f7f7" media="(prefers-color-scheme: light)">
<meta name="theme-color" content="#272727" media="(prefers-color-scheme: dark)">
<!-- Is that even respected? Retrospectively? What a shAItshow…
https://neil-clarke.com/block-the-bots-that-feed-ai-models-by-scraping-your-website/ -->
<meta name="robots" content="noai, noimageai">
<!-- Documented, feel free to shoot an email. -->
<link rel="stylesheet" href="/static/david/css/style_2021-01-20.css">
<!-- See https://www.zachleat.com/web/comprehensive-webfonts/ for the trade-off. -->
<link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t4_poly_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: light), (prefers-color-scheme: no-preference)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t3_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t3_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
<link rel="preload" href="/static/david/css/fonts/triplicate_t3_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
<script>
function toggleTheme(themeName) {
document.documentElement.classList.toggle(
'forced-dark',
themeName === 'dark'
)
document.documentElement.classList.toggle(
'forced-light',
themeName === 'light'
)
}
const selectedTheme = localStorage.getItem('theme')
if (selectedTheme !== 'undefined') {
toggleTheme(selectedTheme)
}
</script>

<meta name="robots" content="noindex, nofollow">
<meta content="origin-when-cross-origin" name="referrer">
<!-- Canonical URL for SEO purposes -->
<link rel="canonical" href="https://andrewkelley.me/post/redis-renamed-to-redict.html">

<body class="remarkdown h1-underline h2-underline h3-underline em-underscore hr-center ul-star pre-tick" data-instant-intensity="viewport-all">


<article>
<header>
<h1>Redis Renamed to Redict</h1>
</header>
<nav>
<p class="center">
<a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
</svg> Accueil</a> •
<a href="https://andrewkelley.me/post/redis-renamed-to-redict.html" title="Lien vers le contenu original">Source originale</a>
<br>
Mis en cache le 2024-03-23
</p>
</nav>
<hr>
<p><a href="https://redict.io/">Redict</a> was originally
created by Salvatore Sanfilippo under the name "Redis".
Around 2018 he started losing interest in the project to pursue a science fiction career and gave
stewardship of the project to <a href="https://en.wikipedia.org/wiki/Redis_(company)">Redis Labs</a>.</p>
<p>
I think that was an unfortunate move because their goal is mainly to extract profit from
the software project rather than to uphold the ideals of Free and Open Source Software.
On March 20, 2024, they
<a href="https://github.com/redis/redis/pull/13157">changed the license to be proprietary</a>,
a widely unpopular action. You know someone is up to no good when they write
"Live long and prosper 🖖" directly above a meme of Darth Vader.
</p>
<p><img class="light" alt="screenshot of the PR merging with many downvotes and darth vader" src="https://andrewkelley.me/img/redis-screenshot-light.png"></p>
<h2 id="license-problems">What are the actual problems with these licenses?</h2>
<p>In short summary, the licenses limit the freedoms of what one can do with
the software in order for Redis Labs to be solely enriched, while asking for
volunteer labor, and having <a href="https://github.com/redis/redis/pull/13157#issuecomment-2014737480">already
benefitted from volunteer labor</a>, that is and was <a href="https://news.ycombinator.com/item?id=39775468">generally offered only
because it enriches <em>everybody</em></a>.</p>
<p><a href="https://ssplisbad.com/">SSPL is BAD</a></p>

<h2 id="legality">Are they allowed to do this?</h2>
<p>All the code before the license change is available under the previous license (BSD-3),
however it is perfectly legal to make further changes to the project under a different license.</p>
<p>This means that it is also legal to <em>fork</em> the project from before the license change,
and continue maintaining the project without the proprietary license change. The only problem
there would be, the project would be missing out on all those juicy future
contributions from Redis Labs... wait a minute, isn't the project already <em>done</em>?
</p>

<h2 id="already-completed">Redict is a Finished Product</h2>
<p>Redict already works great. Lots of companies already use it in production and have
been doing so for many years.</p>
<p>In <a href="https://andrewkelley.me/post/why-we-cant-have-nice-software.html">Why We Can't Have Nice Software</a>,
I point out this pattern of needless software churn in the mindless quest for
profit. This is a perfect example occurring right now. Redict has already reached its
peak; it does not need any more serious software development to occur.
It does not need to <a href="https://redis.com/blog/the-future-of-redis/">pivot
to AI</a>. It can be maintained for decades to come with minimal effort. It can
continue to provide a high amount of value for a low amount of labor. That's
the entire point of software!</p>
<p><strong>Redict does not have any profit left to offer</strong>. It no longer
needs a fund-raising entity behind it anymore. It just needs a good project steward.</p>

<h2 id="drew">Drew DeVault is a Good Steward</h2>
<p><a href="https://drewdevault.com/">Drew</a> is a controversial person, I
think for two reasons.</p>
<p>One, is that he has a record of being rude to many people in the past -
including myself. However, in a <a href="https://lore.kernel.org/lkml/CA+55aFy+Hv9O5citAawS+mVZO+ywCKd9NQ2wxUmGsz9ZJzqgJQ@mail.gmail.com/">similar manner as Linus Torvalds</a>,
Drew has expressed what I can only interpret as sincere regret for such interactions, as
well as a pattern of improved behavior. I was poking through his blog to try to find
example posts of what I mean, and it's difficult to pick them out because he's such a
prolific writer, but perhaps <a href="https://drewdevault.com/2022/05/30/bleh.html">this one</a>
or maybe <a href="https://drewdevault.com/2023/05/01/2023-05-01-Burnout.html">this one</a>.
I'm a strong believer in applying
<a href="https://en.wikipedia.org/wiki/Tit_for_tat">the best game theory strategy</a>
to society: people should have consequences for the harm that they do, but then they should
get a chance to start cooperating again. I can certainly think of "cancelable" things
I have done in the past, that I am thankful are not public, and I cringe every
time I remember them.
</p>

<p>Secondly, and I think this is actually the more important point, Drew has been
an uncompromising advocate of Free and Open Source Software his entire life,
walking the walk more than anyone else I can think of. It's crystal clear that
this is the driving force of his core ideology that determines all of his decision
making. He doesn't budge on any of these principles and it creates conflicts
with people who are trying to exploit FOSS for their own gains. For example, when
you <a href="https://drewdevault.com/2023/07/04/Dont-sign-a-CLA-2.html">call out SourceGraph</a>
you basically piss off everyone who has SourceGraph stock. Do enough of these callouts,
and you've pissed off enough people that there's an entire meme subculture around hating you.</p>

<p>Meanwhile, Drew maintains <a href="https://gitlab.freedesktop.org/wlroots/wlroots">wlroots</a> and <a href="https://swaywm.org/">Sway</a>, and runs <a href="https://sourcehut.org/">a sustainable business</a> on top of
<a href="https://sr.ht/~sircmpwn/sourcehut/">Free and Open Source Software</a>.
SourceHut has a dependency on Redict, so it naturally follows that Drew wants
to keep his supply chain FOSS.</p>

<h2 id="redis-is-the-fork">Redis is the Fork</h2>
<p>
The only thing Redis has going for it, as a software project, is the brand name.
Salvatore is long gone. The active contributors who are working on it are, like I said,
pivoting to AI. Seriously, here's a quote from <a href="https://redis.com/blog/the-future-of-redis/">The Future of Redis</a>:
</p>
<blockquote>
<p>Making Redis the Go-To for Generative AI</p>
<p>we’re staying at the forefront of the GenAI wave</p>
</blockquote>
<p>
Meanwhile, Redict has an actual Free and Open Source Software movement behind
it, spearheaded by Drew DeVault, who has a track record of effective open
source project management.
</p>
<p>
In other words, Redict is the true spiritual successor to what was once Redis.
The title of this blog post is not spicy or edgy; it reflects reality.
</p>
</article>


<hr>

<footer>
<p>
<a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
</svg> Accueil</a> •
<a href="/david/log/" title="Accès au flux RSS"><svg class="icon icon-rss2">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-rss2"></use>
</svg> Suivre</a> •
<a href="http://larlet.com" title="Go to my English profile" data-instant><svg class="icon icon-user-tie">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-user-tie"></use>
</svg> Pro</a> •
<a href="mailto:david%40larlet.fr" title="Envoyer un courriel"><svg class="icon icon-mail">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-mail"></use>
</svg> Email</a> •
<abbr class="nowrap" title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340"><svg class="icon icon-hammer2">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-hammer2"></use>
</svg> Légal</abbr>
</p>
<template id="theme-selector">
<form>
<fieldset>
<legend><svg class="icon icon-brightness-contrast">
<use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-brightness-contrast"></use>
</svg> Thème</legend>
<label>
<input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
</label>
<label>
<input type="radio" value="dark" name="chosen-color-scheme"> Foncé
</label>
<label>
<input type="radio" value="light" name="chosen-color-scheme"> Clair
</label>
</fieldset>
</form>
</template>
</footer>
<script src="/static/david/js/instantpage-5.1.0.min.js" type="module"></script>
<script>
function loadThemeForm(templateName) {
const themeSelectorTemplate = document.querySelector(templateName)
const form = themeSelectorTemplate.content.firstElementChild
themeSelectorTemplate.replaceWith(form)

form.addEventListener('change', (e) => {
const chosenColorScheme = e.target.value
localStorage.setItem('theme', chosenColorScheme)
toggleTheme(chosenColorScheme)
})

const selectedTheme = localStorage.getItem('theme')
if (selectedTheme && selectedTheme !== 'undefined') {
form.querySelector(`[value="${selectedTheme}"]`).checked = true
}
}

const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
window.addEventListener('load', () => {
let hasDarkRules = false
for (const styleSheet of Array.from(document.styleSheets)) {
let mediaRules = []
for (const cssRule of styleSheet.cssRules) {
if (cssRule.type !== CSSRule.MEDIA_RULE) {
continue
}
// WARNING: Safari does not have/supports `conditionText`.
if (cssRule.conditionText) {
if (cssRule.conditionText !== prefersColorSchemeDark) {
continue
}
} else {
if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
continue
}
}
mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
}

// WARNING: do not try to insert a Rule to a styleSheet you are
// currently iterating on, otherwise the browser will be stuck
// in a infinite loop…
for (const mediaRule of mediaRules) {
styleSheet.insertRule(mediaRule.cssText)
hasDarkRules = true
}
}
if (hasDarkRules) {
loadThemeForm('#theme-selector')
}
})
</script>
</body>
</html>

+ 105
- 0
cache/2024/d58c7619eec894c5c069244114ea1df5/index.md View File

@@ -0,0 +1,105 @@
title: Redis Renamed to Redict
url: https://andrewkelley.me/post/redis-renamed-to-redict.html
hash_url: d58c7619eec894c5c069244114ea1df5
archive_date: 2024-03-23
og_image: https://andrewkelley.me/img/redis-screenshot-light.png
description: In other words, Redict is the true spiritual successor to what was once Redis.
favicon:
language: en_US

<p><a href="https://redict.io/">Redict</a> was originally
created by Salvatore Sanfilippo under the name "Redis".
Around 2018 he started losing interest in the project to pursue a science fiction career and gave
stewardship of the project to <a href="https://en.wikipedia.org/wiki/Redis_(company)">Redis Labs</a>.</p>
<p>
I think that was an unfortunate move because their goal is mainly to extract profit from
the software project rather than to uphold the ideals of Free and Open Source Software.
On March 20, 2024, they
<a href="https://github.com/redis/redis/pull/13157">changed the license to be proprietary</a>,
a widely unpopular action. You know someone is up to no good when they write
"Live long and prosper 🖖" directly above a meme of Darth Vader.
</p>
<img class="light" alt="screenshot of the PR merging with many downvotes and darth vader" src="https://andrewkelley.me/img/redis-screenshot-light.png">

<h2 id="license-problems">What are the actual problems with these licenses?</h2>
<p>In short summary, the licenses limit the freedoms of what one can do with
the software in order for Redis Labs to be solely enriched, while asking for
volunteer labor, and having <a href="https://github.com/redis/redis/pull/13157#issuecomment-2014737480">already
benefitted from volunteer labor</a>, that is and was <a href="https://news.ycombinator.com/item?id=39775468">generally offered only
because it enriches <em>everybody</em></a>.</p>
<p><a href="https://ssplisbad.com/">SSPL is BAD</a></p>

<h2 id="legality">Are they allowed to do this?</h2>
<p>All the code before the license change is available under the previous license (BSD-3),
however it is perfectly legal to make further changes to the project under a different license.</p>
<p>This means that it is also legal to <em>fork</em> the project from before the license change,
and continue maintaining the project without the proprietary license change. The only problem
there would be, the project would be missing out on all those juicy future
contributions from Redis Labs... wait a minute, isn't the project already <em>done</em>?
</p>

<h2 id="already-completed">Redict is a Finished Product</h2>
<p>Redict already works great. Lots of companies already use it in production and have
been doing so for many years.</p>
<p>In <a href="https://andrewkelley.me/post/why-we-cant-have-nice-software.html">Why We Can't Have Nice Software</a>,
I point out this pattern of needless software churn in the mindless quest for
profit. This is a perfect example occurring right now. Redict has already reached its
peak; it does not need any more serious software development to occur.
It does not need to <a href="https://redis.com/blog/the-future-of-redis/">pivot
to AI</a>. It can be maintained for decades to come with minimal effort. It can
continue to provide a high amount of value for a low amount of labor. That's
the entire point of software!</p>
<p><strong>Redict does not have any profit left to offer</strong>. It no longer
needs a fund-raising entity behind it anymore. It just needs a good project steward.</p>

<h2 id="drew">Drew DeVault is a Good Steward</h2>
<p><a href="https://drewdevault.com/">Drew</a> is a controversial person, I
think for two reasons.</p>
<p>One, is that he has a record of being rude to many people in the past -
including myself. However, in a <a href="https://lore.kernel.org/lkml/CA+55aFy+Hv9O5citAawS+mVZO+ywCKd9NQ2wxUmGsz9ZJzqgJQ@mail.gmail.com/">similar manner as Linus Torvalds</a>,
Drew has expressed what I can only interpret as sincere regret for such interactions, as
well as a pattern of improved behavior. I was poking through his blog to try to find
example posts of what I mean, and it's difficult to pick them out because he's such a
prolific writer, but perhaps <a href="https://drewdevault.com/2022/05/30/bleh.html">this one</a>
or maybe <a href="https://drewdevault.com/2023/05/01/2023-05-01-Burnout.html">this one</a>.
I'm a strong believer in applying
<a href="https://en.wikipedia.org/wiki/Tit_for_tat">the best game theory strategy</a>
to society: people should have consequences for the harm that they do, but then they should
get a chance to start cooperating again. I can certainly think of "cancelable" things
I have done in the past, that I am thankful are not public, and I cringe every
time I remember them.
</p>

<p>Secondly, and I think this is actually the more important point, Drew has been
an uncompromising advocate of Free and Open Source Software his entire life,
walking the walk more than anyone else I can think of. It's crystal clear that
this is the driving force of his core ideology that determines all of his decision
making. He doesn't budge on any of these principles and it creates conflicts
with people who are trying to exploit FOSS for their own gains. For example, when
you <a href="https://drewdevault.com/2023/07/04/Dont-sign-a-CLA-2.html">call out SourceGraph</a>
you basically piss off everyone who has SourceGraph stock. Do enough of these callouts,
and you've pissed off enough people that there's an entire meme subculture around hating you.</p>

<p>Meanwhile, Drew maintains <a href="https://gitlab.freedesktop.org/wlroots/wlroots">wlroots</a> and <a href="https://swaywm.org/">Sway</a>, and runs <a href="https://sourcehut.org/">a sustainable business</a> on top of
<a href="https://sr.ht/~sircmpwn/sourcehut/">Free and Open Source Software</a>.
SourceHut has a dependency on Redict, so it naturally follows that Drew wants
to keep his supply chain FOSS.</p>

<h2 id="redis-is-the-fork">Redis is the Fork</h2>
<p>
The only thing Redis has going for it, as a software project, is the brand name.
Salvatore is long gone. The active contributors who are working on it are, like I said,
pivoting to AI. Seriously, here's a quote from <a href="https://redis.com/blog/the-future-of-redis/">The Future of Redis</a>:
</p><blockquote>
<p>Making Redis the Go-To for Generative AI</p>
<p>we’re staying at the forefront of the GenAI wave</p>
</blockquote>
<p>
Meanwhile, Redict has an actual Free and Open Source Software movement behind
it, spearheaded by Drew DeVault, who has a track record of effective open
source project management.
</p>
<p>
In other words, Redict is the true spiritual successor to what was once Redis.
The title of this blog post is not spicy or edgy; it reflects reality.
</p>

+ 6
- 0
cache/2024/index.html View File

@@ -76,6 +76,8 @@
<li><a href="/david/cache/2024/f68e9507784b5baf1584085908d60f58/" title="Accès à l’article dans le cache local : Hyperlink escape codes">Hyperlink escape codes</a> (<a href="https://notes.billmill.org/programming/bash/Hyperlink_escape_codes.html" title="Accès à l’article original distant : Hyperlink escape codes">original</a>)</li>
<li><a href="/david/cache/2024/4e116948ed4d26daa981a6c4ea9e4282/" title="Accès à l’article dans le cache local : Write CSS. Not too much. Mostly scoped.">Write CSS. Not too much. Mostly scoped.</a> (<a href="https://www.leereamsnyder.com/write-css-not-too-much-mostly-scoped" title="Accès à l’article original distant : Write CSS. Not too much. Mostly scoped.">original</a>)</li>
<li><a href="/david/cache/2024/c6e0fe933581c74380fa8eaa7f229353/" title="Accès à l’article dans le cache local : Motivation(s) chez Vincent Valentin.">Motivation(s) chez Vincent Valentin.</a> (<a href="https://vincent-valentin.name/articles/motivation-s" title="Accès à l’article original distant : Motivation(s) chez Vincent Valentin.">original</a>)</li>
<li><a href="/david/cache/2024/1d60fc5548a6fe61da80a4e16892fa0c/" title="Accès à l’article dans le cache local : Deep Democracy - IAPOP">Deep Democracy - IAPOP</a> (<a href="https://iapop.com/deep-democracy/" title="Accès à l’article original distant : Deep Democracy - IAPOP">original</a>)</li>
@@ -88,6 +90,8 @@
<li><a href="/david/cache/2024/24716a84007189a332fd8db3e5ff4c05/" title="Accès à l’article dans le cache local : rêve - Carnets Web de La Grange">rêve - Carnets Web de La Grange</a> (<a href="https://www.la-grange.net/2024/02/20/reve" title="Accès à l’article original distant : rêve - Carnets Web de La Grange">original</a>)</li>
<li><a href="/david/cache/2024/d58c7619eec894c5c069244114ea1df5/" title="Accès à l’article dans le cache local : Redis Renamed to Redict">Redis Renamed to Redict</a> (<a href="https://andrewkelley.me/post/redis-renamed-to-redict.html" title="Accès à l’article original distant : Redis Renamed to Redict">original</a>)</li>
<li><a href="/david/cache/2024/bf61b62532f71e39e7b92c76dc36bb0f/" title="Accès à l’article dans le cache local : Popover API (Explainer)">Popover API (Explainer)</a> (<a href="https://open-ui.org/components/popover.research.explainer/" title="Accès à l’article original distant : Popover API (Explainer)">original</a>)</li>
<li><a href="/david/cache/2024/d236f33cf82727313d17cb23bf36a395/" title="Accès à l’article dans le cache local : Reconsider your partnership with Brave">Reconsider your partnership with Brave</a> (<a href="https://kagifeedback.org/d/2808-reconsider-your-partnership-with-brave/6" title="Accès à l’article original distant : Reconsider your partnership with Brave">original</a>)</li>
@@ -140,6 +144,8 @@
<li><a href="/david/cache/2024/590887213b24404c8d1e8355127ce2e2/" title="Accès à l’article dans le cache local : No Maintenance Intended">No Maintenance Intended</a> (<a href="https://unmaintained.tech/" title="Accès à l’article original distant : No Maintenance Intended">original</a>)</li>
<li><a href="/david/cache/2024/0e3d54128711421c0878723dafed66e8/" title="Accès à l’article dans le cache local : Sydney Sweeney, if you're free on Thursday,">Sydney Sweeney, if you're free on Thursday,</a> (<a href="https://youngvulgarian.substack.com/p/sydney-sweeney-if-youre-free-on-thursday" title="Accès à l’article original distant : Sydney Sweeney, if you're free on Thursday,">original</a>)</li>
<li><a href="/david/cache/2024/02eaae467a3a88479393c9fe026f655a/" title="Accès à l’article dans le cache local : CSS :has() Interactive Guide">CSS :has() Interactive Guide</a> (<a href="https://ishadeed.com/article/css-has-guide/" title="Accès à l’article original distant : CSS :has() Interactive Guide">original</a>)</li>
<li><a href="/david/cache/2024/40aada3cc8d6897fda5a277c4299c1fd/" title="Accès à l’article dans le cache local : We Need to Talk About the Front Web">We Need to Talk About the Front Web</a> (<a href="https://gericci.me/we-need-to-talk-about-the-front-web-5.html" title="Accès à l’article original distant : We Need to Talk About the Front Web">original</a>)</li>

Loading…
Cancel
Save