Browse Source

Links

master
David Larlet 2 months ago
parent
commit
751f61fc03
Signed by: David Larlet <david@larlet.fr> GPG Key ID: 3E2953A359E7E7BD

+ 199
- 0
cache/2024/20d288eb47779c4f1b3f36fb86aa7108/index.html View File

@@ -0,0 +1,199 @@
<!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>File over app - Steph Ango (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://stephango.com/file-over-app">

<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>File over app - Steph Ango</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://stephango.com/file-over-app" title="Lien vers le contenu original">Source originale</a>
<br>
Mis en cache le 2024-02-18
</p>
</nav>
<hr>
<p><em>File over app</em> is a philosophy: if you want to create digital artifacts that last, they must be files you can control, in formats that are easy to retrieve and read. Use tools that give you this freedom.</p>

<p><em>File over app</em> is an appeal to tool makers: accept that all software is ephemeral, and give people ownership over their data.</p>

<hr>

<p>In the fullness of time, the files you create are more important than the tools you use to create them. Apps are ephemeral, but your files have a chance to last.</p>

<p>The ancient temples of Egypt contain hieroglyphs that were chiseled in stone thousands of years ago. The ideas hieroglyphs convey are more important than the type of chisel that was used to carve them.</p>

<p>The world is filled with ideas from generations past, transmitted through many mediums, from clay tablets to manuscripts, paintings, sculptures, and tapestries. These artifacts are objects that you can touch, hold, own, store, preserve, and look at. To read something written on paper all you need is eyeballs.</p>

<p>Today, we are creating innumerable digital artifacts, but most of these artifacts are out of our control. They are stored on servers, in databases, gated behind an internet connection, and login to a cloud service. Even the files on your hard drive use proprietary formats that make them incompatible with older systems and other tools.</p>

<p>Paraphrasing something <a href="https://obsidian.md/blog/new-obsidian-icon/" target="_blank" rel="noopener noreferrer">I wrote recently</a></p>

<blockquote>
<p>If you want your writing to still be readable on a computer from the 2060s or 2160s, it’s important that your notes can be read on a computer from the 1960s.</p>
</blockquote>

<p>You should want the files you create to be durable, not only for posterity, but also for your future self. You never know when you might want to go back to something you created years or decades ago. Don’t lock your data into a format you can’t retrieve.</p>

<p>These days I write using an app I help make called <a class="internal-link" href="/obsidian">Obsidian</a>, but it’s a delusion to think it will last forever. The app will eventually become obsolete. It’s the plain text files I create that are designed to last. Who knows if anyone will want to read them besides me, but <em>future me</em> is enough of an audience to make it worthwhile.</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>

+ 32
- 0
cache/2024/20d288eb47779c4f1b3f36fb86aa7108/index.md View File

@@ -0,0 +1,32 @@
title: File over app - Steph Ango
url: https://stephango.com/file-over-app
hash_url: 20d288eb47779c4f1b3f36fb86aa7108
archive_date: 2024-02-18
og_image: https://stephango.com/assets/covers/file-over-app.png
description: If you want to create digital artifacts that last, they must be files you can control, in formats that are easy to retrieve and read. Use tools that give you...
favicon: https://stephango.com/icon.svg
language: en_US

<p><em>File over app</em> is a philosophy: if you want to create digital artifacts that last, they must be files you can control, in formats that are easy to retrieve and read. Use tools that give you this freedom.</p>

<p><em>File over app</em> is an appeal to tool makers: accept that all software is ephemeral, and give people ownership over their data.</p>

<hr>

<p>In the fullness of time, the files you create are more important than the tools you use to create them. Apps are ephemeral, but your files have a chance to last.</p>

<p>The ancient temples of Egypt contain hieroglyphs that were chiseled in stone thousands of years ago. The ideas hieroglyphs convey are more important than the type of chisel that was used to carve them.</p>

<p>The world is filled with ideas from generations past, transmitted through many mediums, from clay tablets to manuscripts, paintings, sculptures, and tapestries. These artifacts are objects that you can touch, hold, own, store, preserve, and look at. To read something written on paper all you need is eyeballs.</p>

<p>Today, we are creating innumerable digital artifacts, but most of these artifacts are out of our control. They are stored on servers, in databases, gated behind an internet connection, and login to a cloud service. Even the files on your hard drive use proprietary formats that make them incompatible with older systems and other tools.</p>

<p>Paraphrasing something <a href="https://obsidian.md/blog/new-obsidian-icon/" target="_blank" rel="noopener noreferrer">I wrote recently</a></p>

<blockquote>
<p>If you want your writing to still be readable on a computer from the 2060s or 2160s, it’s important that your notes can be read on a computer from the 1960s.</p>
</blockquote>

<p>You should want the files you create to be durable, not only for posterity, but also for your future self. You never know when you might want to go back to something you created years or decades ago. Don’t lock your data into a format you can’t retrieve.</p>

<p>These days I write using an app I help make called <a class="internal-link" href="/obsidian">Obsidian</a>, but it’s a delusion to think it will last forever. The app will eventually become obsolete. It’s the plain text files I create that are designed to last. Who knows if anyone will want to read them besides me, but <em>future me</em> is enough of an audience to make it worthwhile.</p>

+ 400
- 0
cache/2024/44c12c8fbb59c7239f0f3b04bae189b7/index.html View File

@@ -0,0 +1,400 @@
<!doctype html><!-- This is a valid HTML5 document. -->
<!-- Screen readers, SEO, extensions and so on. -->
<html lang="">
<!-- 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>Tailwind marketing and misinformation engine (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://nuejs.org/blog/tailwind-misinformation-engine/">

<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>Tailwind marketing and misinformation engine</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://nuejs.org/blog/tailwind-misinformation-engine/" title="Lien vers le contenu original">Source originale</a>
<br>
Mis en cache le 2024-02-18
</p>
</nav>
<hr>
<figure class="author">
<a href="https://nuejs.org/blog/backstory/"><img src="https://nuejs.org/global/img/tero.png"></a>
<figcaption>
<h4>Tero Piirainen</h4>
<p><a href="//twitter.com/tipiirai">@tipiirai</a></p>
</figcaption>
</figure>
<p>Tailwind CSS was born out of this sentence:</p>

<figure class="tall"><img src="https://nuejs.org/img/adam-keynote.jpg" alt="The most reusable components are those with class names that are independent of the content." loading="lazy"><figcaption>Adam's <a href="//youtu.be/CLkxRnRQtDE?t=109">keynote speech</a> in Tailwind Connect 2023</figcaption></figure>
<p>The <a href="//youtu.be/CLkxRnRQtDE?t=109">sentence</a> is from <em>Nicolas Gallagher</em>'s article about <a href="//nicolasgallagher.com/about-html-semantics-front-end-architecture/">HTML semantics and front-end architecture</a>. It was a turning point for <em>Adam Wathan</em>, the creator and frontman of Tailwind. After reading the article he was <a href="https://adamwathan.me/css-utility-classes-and-separation-of-concerns/">"fully convinced that optimizing for reusable CSS was going to be the right choice"</a></p>
<h2 id="origins">Phase 1: The Origins of Tailwind<a href="#origins" title='Permlink for "Phase 1: The Origins of Tailwind"'></a></h2>
<p>Nicholas points out in the article that scalable HTML/CSS must <a href="//nicolasgallagher.com/about-html-semantics-front-end-architecture/">"rely on classes within the HTML to allow for the creation of reusable components"</a>. So instead of using a content-dependent class name like "news", one should use a <strong>content-independent</strong> name like "uilist" or "uilist-item":</p>
<pre glow><code language="html"><i>&lt;</i><strong>nav </strong><b>class</b><i>=</i><em>"<mark>uilist</mark>"</em><i>&gt;</i>
<i>&lt;</i><strong>span </strong><b>class</b><i>=</i><em>"<mark>uilist-item</mark>"</em><i>&gt;</i>
<i>.</i><i>.</i><i>.</i>
<i>&lt;</i><i>/</i><strong>span</strong><i>&gt;</i>
<i>&lt;</i><i>/</i><strong>nav</strong><i>&gt;</i></code></pre>
<p>The more generic the name, the more reusable it is. He used the famous <a href="//www.stubbornella.org/2010/06/25/the-media-object-saves-hundreds-of-lines-of-code/">media object</a> as a prime example of reusable CSS.</p>
<p>But that's not how Adam understood the sentence. Instead of moving towards more reusable class names, he introduced a custom grammar to inline styling rules directly to HTML:</p>

<pre glow><code language="html"><sup>&lt;!-- "uilist" --&gt;</sup>
<i>&lt;</i><strong>div </strong><b>class</b><i>=</i><i>"</i>
<dfn> sticky top<i>-</i><em>0</em> z<i>-</i><em>40</em> w<i>-</i>full backdrop<i>-</i>blur flex<i>-</i>none</dfn>
<dfn> transition<i>-</i>colors duration<i>-</i><em>500</em> <b>lg</b><i>:</i>z<i>-</i><em>50</em> <b>lg</b><i>:</i>border<i>-</i>b</dfn>
<dfn> bg<i>-</i>white<i>/</i><em>95</em> <b>supports-backdrop-blur</b><i>:</i>bg<i>-</i>white<i>/</i><em>60</em></dfn>
<dfn> <b>dark</b><i>:</i>bg<i>-</i>transparent<i>"</i><i>&gt;</i></dfn>

<sup> &lt;!-- "uilist-item" --&gt;</sup>
<i>&lt;</i><strong>span </strong><b>class</b><i>=</i><i>"</i>
<dfn> py<i>-</i><em>4</em> border<i>-</i>b border<i>-</i>slate<i>-</i><em>900</em><i>/</i><em>10</em> <b>lg</b><i>:</i>px<i>-</i><em>8</em></dfn>
<dfn> <b>lg</b><i>:</i>border<i>-</i><em>0</em> <b>dark</b><i>:</i>border<i>-</i>slate<i>-</i><em>300</em><i>/</i><em>10</em> px<i>-</i><em>4</em><i>"</i><i>&gt;</i></dfn>
<i>.</i><i>.</i><i>.</i>
<i>&lt;</i><i>/</i><strong>span</strong><i>&gt;</i>
<i>&lt;</i><i>/</i><strong>div</strong><i>&gt;</i></code></pre>
<p>This was quite a big step away from what Nicolas was saying, who resisted the idea of coupling visual information to elements, like everyone else in the industry back then.</p>
<p>However, in his keynote speech, Adam <a href="//youtu.be/CLkxRnRQtDE?si=mOLOiY8cKLJVb6XZ&amp;t=172">makes us believe</a> that the language he created was a prime example of Nicholas' thinking. And because Nicolas was <a href="//youtu.be/CLkxRnRQtDE?si=af_srSIFIqgmp0mc&amp;t=186">working at Twitter</a>, Adam's take on CSS should work for sites small and large.</p>
<p>On August 7, 2017, Adam wrote an article about <a href="//adamwathan.me/css-utility-classes-and-separation-of-concerns/">CSS utility classes and "Separation of Concerns"</a>. It demonstrates with cleverly chosen examples, how his new creation leads to more maintainable CSS architecture.</p>
<p>But there was a challenge: to make such a statement, he needed to reshape the established CSS best practices. So he introduced <a href="//adamwathan.me/css-utility-classes-and-separation-of-concerns/">new terms</a> to back his contradictory idea:</p>

<figure class="tall"><img src="https://nuejs.org/img/tailwind-practises.png" alt="The new terms and phrases Tailwind developers are familiar with" loading="lazy"><figcaption>The new terms and phrases Tailwind developers are familiar with</figcaption></figure>
<p>The new approach can be summarized as follows:</p>
<blockquote>
<p><a href="//tailwindcss.com/">"Semantic class names” are the reason CSS is hard to maintain</a></p>
</blockquote>
<p>This was a hefty statement as it contradicts with all the prior work and studies about CSS.</p>
<p>In his keynote speech, Adam uses <a href="https://youtu.be/CLkxRnRQtDE?si=s5bmoLnGsmbYDzMA">harsh words</a> to describe the traditional way of structuring CSS, as opposed to how Tailwind is described:</p>

<figure class="tall"><img src="https://nuejs.org/img/villain-and-hero.png" alt="Words used on the keynote speech and Tailwind website" loading="lazy"><figcaption>Words used on the keynote speech and Tailwind website</figcaption></figure>
<p>Old best practices like "semantic", "separation of concerns", or "clean" are usually quoted, which is a common way to question the validity of the word.</p>
<p>Unfair or not, this marketing scheme worked. Developers took the new terms and practices for granted and started tweeting and blogging about them. It was a gold mine for Talwind's commercial business model.</p>
<h2 id="phase2">Phase 2: Utility-first workflow<a href="#phase2" title='Permlink for "Phase 2: Utility-first workflow"'></a></h2>
<p>Once they started cashing, Tailwind wanted to make sure the users were properly onboarded and locked in to the system. They introduced <a href="https://tailwindcss.com/docs/reusing-styles">"utility-first workflow"</a></p>
<blockquote>
<p>Tailwind encourages a utility-first workflow, where designs are implemented using only low-level utility classes. This is a powerful way to avoid premature abstraction and the pain points that come with it.</p>
</blockquote>
<p>Here's how the flow works:</p>
<h3 id="step-1-onboarding">Step 1: Onboarding<a href="#step-1-onboarding" title='Permlink for "Step 1: Onboarding"'></a></h3>
<p>In the utility-first approach, the idea is to "build everything out of utilities, and later extract repeating patterns as they emerge". You are encouraged to try the system. Adam says:</p>
<blockquote>
<p><a href="//tailwindcss.com/">If you give it a chance, I really think you’ll wonder how you ever worked with CSS any other way.</a></p>
</blockquote>
<p>Sounds good, so let's try it.</p>
<p>Once installed, you quickly start to see why people enjoy Tailwind. You can write your styling in the same place as your markup and never think about semantic class names. You feel productive with all the handy shortcuts together with hot-module replacement.</p>
<h3 id="step-2-premature-abstraction">Step 2: "Premature abstraction"<a href="#step-2-premature-abstraction" title='Permlink for "Step 2: "Premature abstraction""'></a></h3>
<p>At some point, hundreds of utilities later, the code you've written doesn't look pretty. You start wondering what comes next after the utility-first step. How to clean things up?</p>
<p>Turns out there is no next step. Or it kind of exists, but it's called "premature abstraction". You can start extracting classes with @apply, but the documentation for <a href="//tailwindcss.com/docs/reusing-styles">reusing styles</a> describes it as a bad practice.</p>
<blockquote>
<p><a href="//tailwindcss.com/docs/reusing-styles#avoiding-premature-abstraction">Whatever you do, don’t use @apply just to make things look “cleaner”</a></p>
</blockquote>
<p>But what should I use @apply for if not for cleaning up? The documentation does not say. It only tells me why it should <strong>not</strong> be used.</p>
<h3 id="step-3-vendor-lock-in">Step 3: Vendor lock-in<a href="#step-3-vendor-lock-in" title='Permlink for "Step 3: Vendor lock-in"'></a></h3>
<p>So I keep coming back to the first step resulting in more and more utility classes. I'm locked inside a loop:</p>

<figure class="tall"><img src="https://nuejs.org/img/utility-first-loop.png" alt="Utility-first workflow" loading="lazy"><figcaption>Utility-first workflow</figcaption></figure>
<p>I find this a rather clever way to lock people using Tailwind, resulting in more retention, loyalty, and money.</p>
<h2 id="catalyst">Phase 3: Catalyst UI kit<a href="#catalyst" title='Permlink for "Phase 3: Catalyst UI kit"'></a></h2>
<p>In December 2023, Tailwind introduced <em>Catalyst</em> with a richer set of language expressions and a React-based UI library.</p>
<h3 id="domain-specific-language-dsl">Domain-specific language (DSL)<a href="#domain-specific-language-dsl" title='Permlink for "Domain-specific language (DSL)"'></a></h3>
<p>To keep up with the ever-evolving CSS standard Tailwind introduced another set of language literals. Over the years Tailwind has grown from a simple set of atoms to a vendor-specific language with expressions, operators, and method calls.</p>
<p>Let's look at the source code of the first button on <a href="//catalyst.tailwindui.com/">Catalyst demo page</a>:</p>

<p><img src="https://nuejs.org/img/tailwind-button.png" loading="lazy"></p>
<section class="small section">
<p>The black button source code. The expressions are sorted alphabetically:</p>

</section>
<pre glow class="small"><code language="html"><i>&lt;</i><strong>button </strong><b>class</b><i>=</i><i>"</i>
<i>[</i><i>&amp;</i>amp<i>;</i><i>&gt;</i><i>[</i><b>data-slot</b><i>=</i>icon<i>]</i><i>]</i><i>:</i><i>-</i>mx<i>-</i><em>0.5</em>
<i>[</i><i>&amp;</i>amp<i>;</i><i>&gt;</i><i>[</i><b>data-slot</b><i>=</i>icon<i>]</i><i>]</i><i>:</i>my<i>-</i><em>0.5</em>
<i>[</i><i>&amp;</i>amp<i>;</i><i>&gt;</i><i>[</i><b>data-slot</b><i>=</i>icon<i>]</i><i>]</i><i>:</i>shrink<i>-</i><em>0</em>
<i>[</i><i>&amp;</i>amp<i>;</i><i>&gt;</i><i>[</i><b>data-slot</b><i>=</i>icon<i>]</i><i>]</i><i>:</i>size<i>-</i><em>5</em>
<i>[</i><i>&amp;</i>amp<i>;</i><i>&gt;</i><i>[</i><b>data-slot</b><i>=</i>icon<i>]</i><i>]</i><i>:</i><b>sm</b><i>:</i>my<i>-</i><em>1</em>
<i>[</i><i>&amp;</i>amp<i>;</i><i>&gt;</i><i>[</i><b>data-slot</b><i>=</i>icon<i>]</i><i>]</i><i>:</i><b>sm</b><i>:</i>size<i>-</i><em>4</em>
<i>[</i><i>&amp;</i>amp<i>;</i><i>&gt;</i><i>[</i><b>data-slot</b><i>=</i>icon<i>]</i><i>]</i><i>:</i><b>text-</b><i>[</i><i>-</i><i>-</i>btn<i>-</i>icon<i>]</i>
<i>[</i><i>-</i><i>-</i><b>btn-bg</b><i>:</i><b>theme</b><i>(</i>colors<i>.</i>zinc<i>.</i><em>900</em><i>)</i><i>]</i>
<i>[</i><i>-</i><i>-</i><b>btn-border</b><i>:</i><b>theme</b><i>(</i>colors<i>.</i>zinc<i>.</i><em>950</em><i>/</i><em>90%</em><i>)</i><i>]</i>
<i>[</i><i>-</i><i>-</i><b>btn-hover-overlay</b><i>:</i><b>theme</b><i>(</i>colors<i>.</i>white<i>/</i><em>10%</em><i>)</i><i>]</i>
<i>[</i><i>-</i><i>-</i><b>btn-icon</b><i>:</i><b>theme</b><i>(</i>colors<i>.</i>zinc<i>.</i><em>400</em><i>)</i><i>]</i>
<b>after</b><i>:</i><i>-</i>z<i>-</i><em>10</em>
<b>after</b><i>:</i>absolute
<b>after</b><i>:</i><b>data-</b><i>[</i>active<i>]</i><i>:</i><b>bg-</b><i>[</i><i>-</i><i>-</i>btn<i>-</i>hover<i>-</i>overlay<i>]</i>
<b>after</b><i>:</i><b>data-</b><i>[</i>disabled<i>]</i><i>:</i>shadow<i>-</i>none
<b>after</b><i>:</i><b>data-</b><i>[</i>hover<i>]</i><i>:</i><b>bg-</b><i>[</i><i>-</i><i>-</i>btn<i>-</i>hover<i>-</i>overlay<i>]</i>
<b>after</b><i>:</i>inset<i>-</i><em>0</em>
<b>after</b><i>:</i><b>rounded-</b><i>[</i><b>calc</b><i>(</i><b>theme</b><i>(</i>borderRadius<i>.</i>lg<i>)</i><i>-</i><em>1px</em><i>)</i><i>]</i>
<b>after</b><i>:</i><b>shadow-</b><i>[</i><b>shadow</b><i>:</i><b>inset_0_1px_theme</b><i>(</i>colors<i>.</i>white<i>/</i><em>15%</em><i>)</i><i>]</i>
<b>before</b><i>:</i><i>-</i>z<i>-</i><em>10</em>
<b>before</b><i>:</i>absolute
<b>before</b><i>:</i><b>bg-</b><i>[</i><i>-</i><i>-</i>btn<i>-</i>bg<i>]</i>
<b>before</b><i>:</i><b>data-</b><i>[</i>disabled<i>]</i><i>:</i>shadow<i>-</i>none
<b>before</b><i>:</i>inset<i>-</i><em>0</em>
<b>before</b><i>:</i><b>rounded-</b><i>[</i><b>calc</b><i>(</i><b>theme</b><i>(</i>borderRadius<i>.</i>lg<i>)</i><i>-</i><em>1px</em><i>)</i><i>]</i>
<b>before</b><i>:</i>shadow
<b>bg-</b><i>[</i><i>-</i><i>-</i>btn<i>-</i>border<i>]</i>
border
border<i>-</i>transparent
<b>dark</b><i>:</i><i>[</i><i>-</i><i>-</i><b>btn-bg</b><i>:</i><b>theme</b><i>(</i>colors<i>.</i>zinc<i>.</i><em>600</em><i>)</i><i>]</i>
<b>dark</b><i>:</i><i>[</i><i>-</i><i>-</i><b>btn-hover-overlay</b><i>:</i><b>theme</b><i>(</i>colors<i>.</i>white<i>/</i><em>5%</em><i>)</i><i>]</i>
<b>dark</b><i>:</i><b>after</b><i>:</i><i>-</i>inset<i>-</i>px
<b>dark</b><i>:</i><b>after</b><i>:</i>rounded<i>-</i>lg
<b>dark</b><i>:</i><b>before</b><i>:</i>hidden
<b>dark</b><i>:</i><b>bg-</b><i>[</i><i>-</i><i>-</i>btn<i>-</i>bg<i>]</i>
<b>dark</b><i>:</i>border<i>-</i>white<i>/</i><em>5</em>
<b>dark</b><i>:</i>text<i>-</i>white
<b>data-</b><i>[</i>active<i>]</i><i>:</i><i>[</i><i>-</i><i>-</i><b>btn-icon</b><i>:</i><b>theme</b><i>(</i>colors<i>.</i>zinc<i>.</i><em>300</em><i>)</i><i>]</i>
<b>data-</b><i>[</i>disabled<i>]</i><i>:</i>opacity<i>-</i><em>50</em>
<b>data-</b><i>[</i>focus<i>]</i><i>:</i>outline
<b>data-</b><i>[</i>focus<i>]</i><i>:</i>outline<i>-</i><em>2</em>
<b>data-</b><i>[</i>focus<i>]</i><i>:</i>outline<i>-</i>blue<i>-</i><em>500</em>
<b>data-</b><i>[</i>focus<i>]</i><i>:</i>outline<i>-</i>offset<i>-</i><em>2</em>
<b>data-</b><i>[</i>hover<i>]</i><i>:</i><i>[</i><i>-</i><i>-</i><b>btn-icon</b><i>:</i><b>theme</b><i>(</i>colors<i>.</i>zinc<i>.</i><em>300</em><i>)</i><i>]</i>
<b>focus</b><i>:</i>outline<i>-</i>none
font<i>-</i>semibold
<b>forced-colors</b><i>:</i><i>[</i><i>-</i><i>-</i><b>btn-icon</b><i>:</i>ButtonText<i>]</i>
<b>forced-colors</b><i>:</i><b>data-</b><i>[</i>hover<i>]</i><i>:</i><i>[</i><i>-</i><i>-</i><b>btn-icon</b><i>:</i>ButtonText<i>]</i>
gap<i>-</i>x<i>-</i><em>2</em>
inline<i>-</i>flex
isolate
items<i>-</i>center
justify<i>-</i>center
<b>px-</b><i>[</i><b>calc</b><i>(</i><b>theme</b><i>(</i><b>spacing</b><i>[</i><em>3.5</em><i>]</i><i>)</i><i>-</i><em>1px</em><i>)</i><i>]</i>
<b>py-</b><i>[</i><b>calc</b><i>(</i><b>theme</b><i>(</i><b>spacing</b><i>[</i><em>2.5</em><i>]</i><i>)</i><i>-</i><em>1px</em><i>)</i><i>]</i>
relative
rounded<i>-</i>lg
<b>sm</b><i>:</i><b>px-</b><i>[</i><b>calc</b><i>(</i><b>theme</b><i>(</i>spacing<i>.</i><em>3</em><i>)</i><i>-</i><em>1px</em><i>)</i><i>]</i>
<b>sm</b><i>:</i><b>py-</b><i>[</i><b>calc</b><i>(</i><b>theme</b><i>(</i><b>spacing</b><i>[</i><em>1.5</em><i>]</i><i>)</i><i>-</i><em>1px</em><i>)</i><i>]</i>
<b>sm</b><i>:</i>text<i>-</i>sm<i>/</i><em>6</em>
text<i>-</i>base<i>/</i><em>6</em>
text<i>-</i>white<i>"</i><i>&gt;</i> Button <i>&lt;</i><i>/</i><strong>button</strong><i>&gt;</i></code></pre>
<p>I have many questions about this:</p>
<p>Most importantly: how is this wall of text more maintainable than a class name like "primary"?</p>
<p>Do I need another wall for the white button?</p>
<p>Also: are there any limits to the utility-first workflow? When can I use @apply to clean things up? After 50 expressions? 100 expressions? 1000?</p>
<h3 id="modeled-after-html">"Modeled after HTML"<a href="#modeled-after-html" title='Permlink for ""Modeled after HTML""'></a></h3>
<p>Another major feature in Catalyst was a new markup language that separates all the language literals behind React components. Here's a dialog example using <a href="//catalyst.tailwindui.com/docs">Catalyst components</a>:</p>

<pre glow><code language="html"><span><i>&lt;</i><strong>Dialog</strong><i>&gt;</i></span>
<span> <i>&lt;</i><strong>DialogTitle</strong><i>&gt;</i>Join mailing list<i>&lt;</i><i>/</i><strong>DialogTitle</strong><i>&gt;</i></span>
<span> <i>&lt;</i><strong>DialogDescription</strong><i>&gt;</i></span>
<span> Expect <i>&lt;</i><strong>Strong</strong><i>&gt;</i>no spamming<i>&lt;</i><i>/</i><strong>Strong</strong><i>&gt;</i></span>
<span> <i>&lt;</i><i>/</i><strong>DialogDescription</strong><i>&gt;</i></span>
<span></span>
<span> <i>&lt;</i><strong>DialogBody</strong><i>&gt;</i></span>
<span> <i>&lt;</i><strong>Field</strong><i>&gt;</i></span>
<span> <i>&lt;</i><strong>Label</strong><i>&gt;</i>Email<i>&lt;</i><i>/</i><strong>Label</strong><i>&gt;</i></span>
<span> <i>&lt;</i><strong>Input </strong><b>name</b><i>=</i><em>"email"</em> <i>/</i><i>&gt;</i></span>
<span> <i>&lt;</i><i>/</i><strong>Field</strong><i>&gt;</i></span>
<span> <i>&lt;</i><i>/</i><strong>DialogBody</strong><i>&gt;</i></span>
<span></span>
<span> <i>&lt;</i><strong>DialogActions</strong><i>&gt;</i></span>
<span> <i>&lt;</i><strong>Button </strong>plain<i>&gt;</i>Cancel<i>&lt;</i><i>/</i><strong>Button</strong><i>&gt;</i></span>
<span> <i>&lt;</i><strong>Button</strong><i>&gt;</i>Join<i>&lt;</i><i>/</i><strong>Button</strong><i>&gt;</i></span>
<span> <i>&lt;</i><i>/</i><strong>DialogActions</strong><i>&gt;</i></span>
<span><i>&lt;</i><i>/</i><strong>Dialog</strong><i>&gt;</i></span></code></pre>
<p>The markup feels surprisingly similar to semantic HTML:</p>

<figure class="tall"><img src="https://nuejs.org/img/catalyst-markup.png" alt="Web standards vs vendor-specific markup" loading="lazy"><figcaption>Web standards vs vendor-specific markup</figcaption></figure>
<p>This raises more questions:</p>
<p>Most importantly: How is <code>&lt;button class="plain"&gt;</code> different from <code>&lt;Button plain&gt;</code>? Isn't this "semantic" — the root of all bad in CSS?</p>
<p>And standard HTML <code>&lt;dialog&gt;</code> is bad, but <code>&lt;Dialog&gt;</code> with uppercase is legit?</p>
<p>Why introduce so many different versions of the <code>&lt;p&gt;</code> tag?</p>
<pre glow><code language="html"><sup>&lt;!-- Catalyst &lt;p&gt; tags --&gt;</sup>
<i>&lt;</i><strong>Text</strong><i>&gt;</i>
<i>&lt;</i><strong>Description</strong><i>&gt;</i>
<i>&lt;</i><strong>DialogDescription</strong><i>&gt;</i>
<i>&lt;</i><strong>AlertDescription</strong><i>&gt;</i>
<i>.</i><i>.</i><i>.</i></code></pre>
<p>Why is content-aware naming okay in element names but bad in class names?</p>
<p>Is separation of concerns suddenly okay with Catalyst, but bad with vanilla HTML and CSS?</p>
<p>I'm confused, to say the least.</p>
<hr>
<h2 id="i-love-css">I love ❤️ CSS<a href="#i-love-css" title='Permlink for "I love ❤️ CSS"'></a></h2>
<p>I started web development at the age of a <code>&lt;blink&gt;</code> tag and CSS has always been my favorite part of the web development stack. I'm particularly fascinated about the crossing between design and <a href="https://bradfrost.com/blog/post/front-of-the-front-end-and-back-of-the-front-end-web-development/">front-of-the frontend</a>.</p>
<p>When Microsoft released <a href="https://en.wikipedia.org/wiki/Internet_Explorer_4">Internet Explorer 4.0</a> with solid support for both external stylesheets and DHTML, It nailed me to the separation of concerns pattern. I see it as the most important component for software scalability and it's particularly important with HTML and CSS. The way of organizing design has been around for centuries: there are element types and contexts. The nuanced relationship between <a href="//en.wikipedia.org/wiki/Form_follows_function">form and function</a>. CSS is the missing tool to bring foundational design-thinking to frontend development.</p>
<p>Fast forward to this date, and the solid foundation has almost disappeared. Styling is inlined and CSS is written with JavaScript. There are no element types, nor contexts. Styling is flat and not cascading. Global is feared instead of used.</p>
<p>We're using maybe 30% of the full potential.</p>
<p>I'm not a fan of any of that.</p>
<p>I recommend everyone to take a closer look to what has happened to CSS there in the past 10 years. Regardless of your current stance. It's a powerful language that far surpasses the capabilities of Tailwind. Learn to build scaleable architectures, and see how atomic class names and inline styling fit into the bigger picture.</p>
<h3 id="first-things-first-learn-css">First things first: Learn CSS<a href="#first-things-first-learn-css" title='Permlink for "First things first: Learn CSS"'></a></h3>
<p>The first step is to learn CSS. It's the ultimate design language for the web. A safe bet for years to come.</p>
<ol>
<li><p>Start from the <a href="//nicolasgallagher.com/about-html-semantics-front-end-architecture/">Nicholas's post</a> and learn the benefits of semantic naming. Understand how Adam cherry-picked one sentence and misused it to validate the contrasting practises of Tailwind.</p>
</li>
<li><p>Study MDN documentation on web standards. There's a lot, so start with the most important aspects of CSS: <a href="//developer.mozilla.org/en-US/docs/Web/CSS/Cascade">the cascade</a> and <a href="//developer.mozilla.org/en-US/docs/Web/CSS/CSS_nesting/Nesting_and_specificity">specificity</a>.</p>
</li>
<li><p>Take inspiration. Learn how the best developers in the game like <a href="//ryanmulligan.dev/blog/">Ryan Mulligan</a>, <a href="//ishadeed.com/">Ahmad Shadeed</a>, and <a href="//www.joshwcomeau.com/">Josh Comeau</a> use CSS in more stylish, and creative ways.</p>
</li>
</ol>
<h3 id="content-first">Content first<a href="#content-first" title='Permlink for "Content first"'></a></h3>
<p>Here's a better workflow. It has many names: "standards first", "content first", or "progressive enhancement".</p>

<figure class="tall"><img src="https://nuejs.org/img/standards-first.png" alt="Standards first model" loading="lazy"><figcaption>Standards first model</figcaption></figure>
<p>You start with a pure, semantic layout and figure out all the reusable pieces of CSS. At times, especially when building new components, you might want to prototype quickly with inline styling. But that's okay and part of the system. You can clean things up later.</p>
<blockquote>
<p>Clean code is easier to maintain</p>
</blockquote>
<p>There are no "pain points" in clean code, only benefits. This is the system I want to teach to my kids. I want them to understand how web standards work, and where all the trends come from.</p>
<p>Because trends are temporary, but standards are forever.</p>
<h3 id="stay-relevant">Stay relevant<a href="#stay-relevant" title='Permlink for "Stay relevant"'></a></h3>
<p>My guess: It's only a matter of time before Tailwind collapses. The vendor-specific language and the misleading communication cannot hold water very long. The utility soup produced today will eventually turn into a technical debt. The next generation looks back and asks: "You actually wrote <strong>that</strong>?"</p>
<p>Learn to write clean HTML and CSS and stay relevant for years to come.</p>

<p><img src="https://nuejs.org/img/tw-switch.png" loading="lazy" class="tall"></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>

+ 231
- 0
cache/2024/44c12c8fbb59c7239f0f3b04bae189b7/index.md View File

@@ -0,0 +1,231 @@
title: Tailwind marketing and misinformation engine
url: https://nuejs.org/blog/tailwind-misinformation-engine/
hash_url: 44c12c8fbb59c7239f0f3b04bae189b7
archive_date: 2024-02-18
og_image: https://nuejs.org/img/tailwind-messaging-pillars.jpg
description: The origins of Tailwind and how it is framed against semantic CSS
favicon: https://nuejs.org/img/favicon.svg
language:

<figure class="author">
<a href="https://nuejs.org/blog/backstory/"><img src="https://nuejs.org/global/img/tero.png"></a>
<figcaption>
<h4>Tero Piirainen</h4>
<p><a href="//twitter.com/tipiirai">@tipiirai</a></p>
</figcaption>
</figure>
<p>Tailwind CSS was born out of this sentence:</p>

<figure class="tall"><img src="https://nuejs.org/img/adam-keynote.jpg" alt="The most reusable components are those with class names that are independent of the content." loading="lazy"><figcaption>Adam's <a href="//youtu.be/CLkxRnRQtDE?t=109">keynote speech</a> in Tailwind Connect 2023</figcaption></figure>
<p>The <a href="//youtu.be/CLkxRnRQtDE?t=109">sentence</a> is from <em>Nicolas Gallagher</em>'s article about <a href="//nicolasgallagher.com/about-html-semantics-front-end-architecture/">HTML semantics and front-end architecture</a>. It was a turning point for <em>Adam Wathan</em>, the creator and frontman of Tailwind. After reading the article he was <a href="https://adamwathan.me/css-utility-classes-and-separation-of-concerns/">"fully convinced that optimizing for reusable CSS was going to be the right choice"</a></p>
<h2 id="origins">Phase 1: The Origins of Tailwind<a href="#origins" title='Permlink for "Phase 1: The Origins of Tailwind"'></a></h2>
<p>Nicholas points out in the article that scalable HTML/CSS must <a href="//nicolasgallagher.com/about-html-semantics-front-end-architecture/">"rely on classes within the HTML to allow for the creation of reusable components"</a>. So instead of using a content-dependent class name like "news", one should use a <strong>content-independent</strong> name like "uilist" or "uilist-item":</p>
<pre glow><code language="html"><i>&lt;</i><strong>nav </strong><b>class</b><i>=</i><em>"<mark>uilist</mark>"</em><i>&gt;</i>
<i>&lt;</i><strong>span </strong><b>class</b><i>=</i><em>"<mark>uilist-item</mark>"</em><i>&gt;</i>
<i>.</i><i>.</i><i>.</i>
<i>&lt;</i><i>/</i><strong>span</strong><i>&gt;</i>
<i>&lt;</i><i>/</i><strong>nav</strong><i>&gt;</i></code></pre><p>The more generic the name, the more reusable it is. He used the famous <a href="//www.stubbornella.org/2010/06/25/the-media-object-saves-hundreds-of-lines-of-code/">media object</a> as a prime example of reusable CSS.</p>
<p>But that's not how Adam understood the sentence. Instead of moving towards more reusable class names, he introduced a custom grammar to inline styling rules directly to HTML:</p>

<pre glow><code language="html"><sup>&lt;!-- "uilist" --&gt;</sup>
<i>&lt;</i><strong>div </strong><b>class</b><i>=</i><i>"</i>
<dfn> sticky top<i>-</i><em>0</em> z<i>-</i><em>40</em> w<i>-</i>full backdrop<i>-</i>blur flex<i>-</i>none</dfn>
<dfn> transition<i>-</i>colors duration<i>-</i><em>500</em> <b>lg</b><i>:</i>z<i>-</i><em>50</em> <b>lg</b><i>:</i>border<i>-</i>b</dfn>
<dfn> bg<i>-</i>white<i>/</i><em>95</em> <b>supports-backdrop-blur</b><i>:</i>bg<i>-</i>white<i>/</i><em>60</em></dfn>
<dfn> <b>dark</b><i>:</i>bg<i>-</i>transparent<i>"</i><i>&gt;</i></dfn>

<sup> &lt;!-- "uilist-item" --&gt;</sup>
<i>&lt;</i><strong>span </strong><b>class</b><i>=</i><i>"</i>
<dfn> py<i>-</i><em>4</em> border<i>-</i>b border<i>-</i>slate<i>-</i><em>900</em><i>/</i><em>10</em> <b>lg</b><i>:</i>px<i>-</i><em>8</em></dfn>
<dfn> <b>lg</b><i>:</i>border<i>-</i><em>0</em> <b>dark</b><i>:</i>border<i>-</i>slate<i>-</i><em>300</em><i>/</i><em>10</em> px<i>-</i><em>4</em><i>"</i><i>&gt;</i></dfn>
<i>.</i><i>.</i><i>.</i>
<i>&lt;</i><i>/</i><strong>span</strong><i>&gt;</i>
<i>&lt;</i><i>/</i><strong>div</strong><i>&gt;</i></code></pre>
<p>This was quite a big step away from what Nicolas was saying, who resisted the idea of coupling visual information to elements, like everyone else in the industry back then.</p>
<p>However, in his keynote speech, Adam <a href="//youtu.be/CLkxRnRQtDE?si=mOLOiY8cKLJVb6XZ&amp;t=172">makes us believe</a> that the language he created was a prime example of Nicholas' thinking. And because Nicolas was <a href="//youtu.be/CLkxRnRQtDE?si=af_srSIFIqgmp0mc&amp;t=186">working at Twitter</a>, Adam's take on CSS should work for sites small and large.</p>
<p>On August 7, 2017, Adam wrote an article about <a href="//adamwathan.me/css-utility-classes-and-separation-of-concerns/">CSS utility classes and "Separation of Concerns"</a>. It demonstrates with cleverly chosen examples, how his new creation leads to more maintainable CSS architecture.</p>
<p>But there was a challenge: to make such a statement, he needed to reshape the established CSS best practices. So he introduced <a href="//adamwathan.me/css-utility-classes-and-separation-of-concerns/">new terms</a> to back his contradictory idea:</p>

<figure class="tall"><img src="https://nuejs.org/img/tailwind-practises.png" alt="The new terms and phrases Tailwind developers are familiar with" loading="lazy"><figcaption>The new terms and phrases Tailwind developers are familiar with</figcaption></figure>
<p>The new approach can be summarized as follows:</p>
<blockquote>
<p><a href="//tailwindcss.com/">"Semantic class names” are the reason CSS is hard to maintain</a></p>
</blockquote>
<p>This was a hefty statement as it contradicts with all the prior work and studies about CSS.</p>
<p>In his keynote speech, Adam uses <a href="https://youtu.be/CLkxRnRQtDE?si=s5bmoLnGsmbYDzMA">harsh words</a> to describe the traditional way of structuring CSS, as opposed to how Tailwind is described:</p>

<figure class="tall"><img src="https://nuejs.org/img/villain-and-hero.png" alt="Words used on the keynote speech and Tailwind website" loading="lazy"><figcaption>Words used on the keynote speech and Tailwind website</figcaption></figure>
<p>Old best practices like "semantic", "separation of concerns", or "clean" are usually quoted, which is a common way to question the validity of the word.</p>
<p>Unfair or not, this marketing scheme worked. Developers took the new terms and practices for granted and started tweeting and blogging about them. It was a gold mine for Talwind's commercial business model.</p>
<h2 id="phase2">Phase 2: Utility-first workflow<a href="#phase2" title='Permlink for "Phase 2: Utility-first workflow"'></a></h2>
<p>Once they started cashing, Tailwind wanted to make sure the users were properly onboarded and locked in to the system. They introduced <a href="https://tailwindcss.com/docs/reusing-styles">"utility-first workflow"</a></p>
<blockquote>
<p>Tailwind encourages a utility-first workflow, where designs are implemented using only low-level utility classes. This is a powerful way to avoid premature abstraction and the pain points that come with it.</p>
</blockquote>
<p>Here's how the flow works:</p>
<h3 id="step-1-onboarding">Step 1: Onboarding<a href="#step-1-onboarding" title='Permlink for "Step 1: Onboarding"'></a></h3>
<p>In the utility-first approach, the idea is to "build everything out of utilities, and later extract repeating patterns as they emerge". You are encouraged to try the system. Adam says:</p>
<blockquote>
<p><a href="//tailwindcss.com/">If you give it a chance, I really think you’ll wonder how you ever worked with CSS any other way.</a></p>
</blockquote>
<p>Sounds good, so let's try it.</p>
<p>Once installed, you quickly start to see why people enjoy Tailwind. You can write your styling in the same place as your markup and never think about semantic class names. You feel productive with all the handy shortcuts together with hot-module replacement.</p>
<h3 id="step-2-premature-abstraction">Step 2: "Premature abstraction"<a href="#step-2-premature-abstraction" title='Permlink for "Step 2: "Premature abstraction""'></a></h3>
<p>At some point, hundreds of utilities later, the code you've written doesn't look pretty. You start wondering what comes next after the utility-first step. How to clean things up?</p>
<p>Turns out there is no next step. Or it kind of exists, but it's called "premature abstraction". You can start extracting classes with @apply, but the documentation for <a href="//tailwindcss.com/docs/reusing-styles">reusing styles</a> describes it as a bad practice.</p>
<blockquote>
<p><a href="//tailwindcss.com/docs/reusing-styles#avoiding-premature-abstraction">Whatever you do, don’t use @apply just to make things look “cleaner”</a></p>
</blockquote>
<p>But what should I use @apply for if not for cleaning up? The documentation does not say. It only tells me why it should <strong>not</strong> be used.</p>
<h3 id="step-3-vendor-lock-in">Step 3: Vendor lock-in<a href="#step-3-vendor-lock-in" title='Permlink for "Step 3: Vendor lock-in"'></a></h3>
<p>So I keep coming back to the first step resulting in more and more utility classes. I'm locked inside a loop:</p>

<figure class="tall"><img src="https://nuejs.org/img/utility-first-loop.png" alt="Utility-first workflow" loading="lazy"><figcaption>Utility-first workflow</figcaption></figure>
<p>I find this a rather clever way to lock people using Tailwind, resulting in more retention, loyalty, and money.</p>
<h2 id="catalyst">Phase 3: Catalyst UI kit<a href="#catalyst" title='Permlink for "Phase 3: Catalyst UI kit"'></a></h2>
<p>In December 2023, Tailwind introduced <em>Catalyst</em> with a richer set of language expressions and a React-based UI library.</p>
<h3 id="domain-specific-language-dsl">Domain-specific language (DSL)<a href="#domain-specific-language-dsl" title='Permlink for "Domain-specific language (DSL)"'></a></h3>
<p>To keep up with the ever-evolving CSS standard Tailwind introduced another set of language literals. Over the years Tailwind has grown from a simple set of atoms to a vendor-specific language with expressions, operators, and method calls.</p>
<p>Let's look at the source code of the first button on <a href="//catalyst.tailwindui.com/">Catalyst demo page</a>:</p>

<img src="https://nuejs.org/img/tailwind-button.png" loading="lazy">
<section class="small section">
<p>The black button source code. The expressions are sorted alphabetically:</p>

</section>
<pre glow class="small"><code language="html"><i>&lt;</i><strong>button </strong><b>class</b><i>=</i><i>"</i>
<i>[</i><i>&amp;</i>amp<i>;</i><i>&gt;</i><i>[</i><b>data-slot</b><i>=</i>icon<i>]</i><i>]</i><i>:</i><i>-</i>mx<i>-</i><em>0.5</em>
<i>[</i><i>&amp;</i>amp<i>;</i><i>&gt;</i><i>[</i><b>data-slot</b><i>=</i>icon<i>]</i><i>]</i><i>:</i>my<i>-</i><em>0.5</em>
<i>[</i><i>&amp;</i>amp<i>;</i><i>&gt;</i><i>[</i><b>data-slot</b><i>=</i>icon<i>]</i><i>]</i><i>:</i>shrink<i>-</i><em>0</em>
<i>[</i><i>&amp;</i>amp<i>;</i><i>&gt;</i><i>[</i><b>data-slot</b><i>=</i>icon<i>]</i><i>]</i><i>:</i>size<i>-</i><em>5</em>
<i>[</i><i>&amp;</i>amp<i>;</i><i>&gt;</i><i>[</i><b>data-slot</b><i>=</i>icon<i>]</i><i>]</i><i>:</i><b>sm</b><i>:</i>my<i>-</i><em>1</em>
<i>[</i><i>&amp;</i>amp<i>;</i><i>&gt;</i><i>[</i><b>data-slot</b><i>=</i>icon<i>]</i><i>]</i><i>:</i><b>sm</b><i>:</i>size<i>-</i><em>4</em>
<i>[</i><i>&amp;</i>amp<i>;</i><i>&gt;</i><i>[</i><b>data-slot</b><i>=</i>icon<i>]</i><i>]</i><i>:</i><b>text-</b><i>[</i><i>-</i><i>-</i>btn<i>-</i>icon<i>]</i>
<i>[</i><i>-</i><i>-</i><b>btn-bg</b><i>:</i><b>theme</b><i>(</i>colors<i>.</i>zinc<i>.</i><em>900</em><i>)</i><i>]</i>
<i>[</i><i>-</i><i>-</i><b>btn-border</b><i>:</i><b>theme</b><i>(</i>colors<i>.</i>zinc<i>.</i><em>950</em><i>/</i><em>90%</em><i>)</i><i>]</i>
<i>[</i><i>-</i><i>-</i><b>btn-hover-overlay</b><i>:</i><b>theme</b><i>(</i>colors<i>.</i>white<i>/</i><em>10%</em><i>)</i><i>]</i>
<i>[</i><i>-</i><i>-</i><b>btn-icon</b><i>:</i><b>theme</b><i>(</i>colors<i>.</i>zinc<i>.</i><em>400</em><i>)</i><i>]</i>
<b>after</b><i>:</i><i>-</i>z<i>-</i><em>10</em>
<b>after</b><i>:</i>absolute
<b>after</b><i>:</i><b>data-</b><i>[</i>active<i>]</i><i>:</i><b>bg-</b><i>[</i><i>-</i><i>-</i>btn<i>-</i>hover<i>-</i>overlay<i>]</i>
<b>after</b><i>:</i><b>data-</b><i>[</i>disabled<i>]</i><i>:</i>shadow<i>-</i>none
<b>after</b><i>:</i><b>data-</b><i>[</i>hover<i>]</i><i>:</i><b>bg-</b><i>[</i><i>-</i><i>-</i>btn<i>-</i>hover<i>-</i>overlay<i>]</i>
<b>after</b><i>:</i>inset<i>-</i><em>0</em>
<b>after</b><i>:</i><b>rounded-</b><i>[</i><b>calc</b><i>(</i><b>theme</b><i>(</i>borderRadius<i>.</i>lg<i>)</i><i>-</i><em>1px</em><i>)</i><i>]</i>
<b>after</b><i>:</i><b>shadow-</b><i>[</i><b>shadow</b><i>:</i><b>inset_0_1px_theme</b><i>(</i>colors<i>.</i>white<i>/</i><em>15%</em><i>)</i><i>]</i>
<b>before</b><i>:</i><i>-</i>z<i>-</i><em>10</em>
<b>before</b><i>:</i>absolute
<b>before</b><i>:</i><b>bg-</b><i>[</i><i>-</i><i>-</i>btn<i>-</i>bg<i>]</i>
<b>before</b><i>:</i><b>data-</b><i>[</i>disabled<i>]</i><i>:</i>shadow<i>-</i>none
<b>before</b><i>:</i>inset<i>-</i><em>0</em>
<b>before</b><i>:</i><b>rounded-</b><i>[</i><b>calc</b><i>(</i><b>theme</b><i>(</i>borderRadius<i>.</i>lg<i>)</i><i>-</i><em>1px</em><i>)</i><i>]</i>
<b>before</b><i>:</i>shadow
<b>bg-</b><i>[</i><i>-</i><i>-</i>btn<i>-</i>border<i>]</i>
border
border<i>-</i>transparent
<b>dark</b><i>:</i><i>[</i><i>-</i><i>-</i><b>btn-bg</b><i>:</i><b>theme</b><i>(</i>colors<i>.</i>zinc<i>.</i><em>600</em><i>)</i><i>]</i>
<b>dark</b><i>:</i><i>[</i><i>-</i><i>-</i><b>btn-hover-overlay</b><i>:</i><b>theme</b><i>(</i>colors<i>.</i>white<i>/</i><em>5%</em><i>)</i><i>]</i>
<b>dark</b><i>:</i><b>after</b><i>:</i><i>-</i>inset<i>-</i>px
<b>dark</b><i>:</i><b>after</b><i>:</i>rounded<i>-</i>lg
<b>dark</b><i>:</i><b>before</b><i>:</i>hidden
<b>dark</b><i>:</i><b>bg-</b><i>[</i><i>-</i><i>-</i>btn<i>-</i>bg<i>]</i>
<b>dark</b><i>:</i>border<i>-</i>white<i>/</i><em>5</em>
<b>dark</b><i>:</i>text<i>-</i>white
<b>data-</b><i>[</i>active<i>]</i><i>:</i><i>[</i><i>-</i><i>-</i><b>btn-icon</b><i>:</i><b>theme</b><i>(</i>colors<i>.</i>zinc<i>.</i><em>300</em><i>)</i><i>]</i>
<b>data-</b><i>[</i>disabled<i>]</i><i>:</i>opacity<i>-</i><em>50</em>
<b>data-</b><i>[</i>focus<i>]</i><i>:</i>outline
<b>data-</b><i>[</i>focus<i>]</i><i>:</i>outline<i>-</i><em>2</em>
<b>data-</b><i>[</i>focus<i>]</i><i>:</i>outline<i>-</i>blue<i>-</i><em>500</em>
<b>data-</b><i>[</i>focus<i>]</i><i>:</i>outline<i>-</i>offset<i>-</i><em>2</em>
<b>data-</b><i>[</i>hover<i>]</i><i>:</i><i>[</i><i>-</i><i>-</i><b>btn-icon</b><i>:</i><b>theme</b><i>(</i>colors<i>.</i>zinc<i>.</i><em>300</em><i>)</i><i>]</i>
<b>focus</b><i>:</i>outline<i>-</i>none
font<i>-</i>semibold
<b>forced-colors</b><i>:</i><i>[</i><i>-</i><i>-</i><b>btn-icon</b><i>:</i>ButtonText<i>]</i>
<b>forced-colors</b><i>:</i><b>data-</b><i>[</i>hover<i>]</i><i>:</i><i>[</i><i>-</i><i>-</i><b>btn-icon</b><i>:</i>ButtonText<i>]</i>
gap<i>-</i>x<i>-</i><em>2</em>
inline<i>-</i>flex
isolate
items<i>-</i>center
justify<i>-</i>center
<b>px-</b><i>[</i><b>calc</b><i>(</i><b>theme</b><i>(</i><b>spacing</b><i>[</i><em>3.5</em><i>]</i><i>)</i><i>-</i><em>1px</em><i>)</i><i>]</i>
<b>py-</b><i>[</i><b>calc</b><i>(</i><b>theme</b><i>(</i><b>spacing</b><i>[</i><em>2.5</em><i>]</i><i>)</i><i>-</i><em>1px</em><i>)</i><i>]</i>
relative
rounded<i>-</i>lg
<b>sm</b><i>:</i><b>px-</b><i>[</i><b>calc</b><i>(</i><b>theme</b><i>(</i>spacing<i>.</i><em>3</em><i>)</i><i>-</i><em>1px</em><i>)</i><i>]</i>
<b>sm</b><i>:</i><b>py-</b><i>[</i><b>calc</b><i>(</i><b>theme</b><i>(</i><b>spacing</b><i>[</i><em>1.5</em><i>]</i><i>)</i><i>-</i><em>1px</em><i>)</i><i>]</i>
<b>sm</b><i>:</i>text<i>-</i>sm<i>/</i><em>6</em>
text<i>-</i>base<i>/</i><em>6</em>
text<i>-</i>white<i>"</i><i>&gt;</i> Button <i>&lt;</i><i>/</i><strong>button</strong><i>&gt;</i></code></pre>
<p>I have many questions about this:</p>
<p>Most importantly: how is this wall of text more maintainable than a class name like "primary"?</p>
<p>Do I need another wall for the white button?</p>
<p>Also: are there any limits to the utility-first workflow? When can I use @apply to clean things up? After 50 expressions? 100 expressions? 1000?</p>
<h3 id="modeled-after-html">"Modeled after HTML"<a href="#modeled-after-html" title='Permlink for ""Modeled after HTML""'></a></h3>
<p>Another major feature in Catalyst was a new markup language that separates all the language literals behind React components. Here's a dialog example using <a href="//catalyst.tailwindui.com/docs">Catalyst components</a>:</p>

<pre glow><code language="html"><span><i>&lt;</i><strong>Dialog</strong><i>&gt;</i></span>
<span> <i>&lt;</i><strong>DialogTitle</strong><i>&gt;</i>Join mailing list<i>&lt;</i><i>/</i><strong>DialogTitle</strong><i>&gt;</i></span>
<span> <i>&lt;</i><strong>DialogDescription</strong><i>&gt;</i></span>
<span> Expect <i>&lt;</i><strong>Strong</strong><i>&gt;</i>no spamming<i>&lt;</i><i>/</i><strong>Strong</strong><i>&gt;</i></span>
<span> <i>&lt;</i><i>/</i><strong>DialogDescription</strong><i>&gt;</i></span>
<span></span>
<span> <i>&lt;</i><strong>DialogBody</strong><i>&gt;</i></span>
<span> <i>&lt;</i><strong>Field</strong><i>&gt;</i></span>
<span> <i>&lt;</i><strong>Label</strong><i>&gt;</i>Email<i>&lt;</i><i>/</i><strong>Label</strong><i>&gt;</i></span>
<span> <i>&lt;</i><strong>Input </strong><b>name</b><i>=</i><em>"email"</em> <i>/</i><i>&gt;</i></span>
<span> <i>&lt;</i><i>/</i><strong>Field</strong><i>&gt;</i></span>
<span> <i>&lt;</i><i>/</i><strong>DialogBody</strong><i>&gt;</i></span>
<span></span>
<span> <i>&lt;</i><strong>DialogActions</strong><i>&gt;</i></span>
<span> <i>&lt;</i><strong>Button </strong>plain<i>&gt;</i>Cancel<i>&lt;</i><i>/</i><strong>Button</strong><i>&gt;</i></span>
<span> <i>&lt;</i><strong>Button</strong><i>&gt;</i>Join<i>&lt;</i><i>/</i><strong>Button</strong><i>&gt;</i></span>
<span> <i>&lt;</i><i>/</i><strong>DialogActions</strong><i>&gt;</i></span>
<span><i>&lt;</i><i>/</i><strong>Dialog</strong><i>&gt;</i></span></code></pre>
<p>The markup feels surprisingly similar to semantic HTML:</p>

<figure class="tall"><img src="https://nuejs.org/img/catalyst-markup.png" alt="Web standards vs vendor-specific markup" loading="lazy"><figcaption>Web standards vs vendor-specific markup</figcaption></figure>
<p>This raises more questions:</p>
<p>Most importantly: How is <code>&lt;button class="plain"&gt;</code> different from <code>&lt;Button plain&gt;</code>? Isn't this "semantic" — the root of all bad in CSS?</p>
<p>And standard HTML <code>&lt;dialog&gt;</code> is bad, but <code>&lt;Dialog&gt;</code> with uppercase is legit?</p>
<p>Why introduce so many different versions of the <code>&lt;p&gt;</code> tag?</p>
<pre glow><code language="html"><sup>&lt;!-- Catalyst &lt;p&gt; tags --&gt;</sup>
<i>&lt;</i><strong>Text</strong><i>&gt;</i>
<i>&lt;</i><strong>Description</strong><i>&gt;</i>
<i>&lt;</i><strong>DialogDescription</strong><i>&gt;</i>
<i>&lt;</i><strong>AlertDescription</strong><i>&gt;</i>
<i>.</i><i>.</i><i>.</i></code></pre><p>Why is content-aware naming okay in element names but bad in class names?</p>
<p>Is separation of concerns suddenly okay with Catalyst, but bad with vanilla HTML and CSS?</p>
<p>I'm confused, to say the least.</p>
<hr>
<h2 id="i-love-css">I love ❤️ CSS<a href="#i-love-css" title='Permlink for "I love ❤️ CSS"'></a></h2>
<p>I started web development at the age of a <code>&lt;blink&gt;</code> tag and CSS has always been my favorite part of the web development stack. I'm particularly fascinated about the crossing between design and <a href="https://bradfrost.com/blog/post/front-of-the-front-end-and-back-of-the-front-end-web-development/">front-of-the frontend</a>.</p>
<p>When Microsoft released <a href="https://en.wikipedia.org/wiki/Internet_Explorer_4">Internet Explorer 4.0</a> with solid support for both external stylesheets and DHTML, It nailed me to the separation of concerns pattern. I see it as the most important component for software scalability and it's particularly important with HTML and CSS. The way of organizing design has been around for centuries: there are element types and contexts. The nuanced relationship between <a href="//en.wikipedia.org/wiki/Form_follows_function">form and function</a>. CSS is the missing tool to bring foundational design-thinking to frontend development.</p>
<p>Fast forward to this date, and the solid foundation has almost disappeared. Styling is inlined and CSS is written with JavaScript. There are no element types, nor contexts. Styling is flat and not cascading. Global is feared instead of used.</p>
<p>We're using maybe 30% of the full potential.</p>
<p>I'm not a fan of any of that.</p>
<p>I recommend everyone to take a closer look to what has happened to CSS there in the past 10 years. Regardless of your current stance. It's a powerful language that far surpasses the capabilities of Tailwind. Learn to build scaleable architectures, and see how atomic class names and inline styling fit into the bigger picture.</p>
<h3 id="first-things-first-learn-css">First things first: Learn CSS<a href="#first-things-first-learn-css" title='Permlink for "First things first: Learn CSS"'></a></h3>
<p>The first step is to learn CSS. It's the ultimate design language for the web. A safe bet for years to come.</p>
<ol>
<li><p>Start from the <a href="//nicolasgallagher.com/about-html-semantics-front-end-architecture/">Nicholas's post</a> and learn the benefits of semantic naming. Understand how Adam cherry-picked one sentence and misused it to validate the contrasting practises of Tailwind.</p>
</li>
<li><p>Study MDN documentation on web standards. There's a lot, so start with the most important aspects of CSS: <a href="//developer.mozilla.org/en-US/docs/Web/CSS/Cascade">the cascade</a> and <a href="//developer.mozilla.org/en-US/docs/Web/CSS/CSS_nesting/Nesting_and_specificity">specificity</a>.</p>
</li>
<li><p>Take inspiration. Learn how the best developers in the game like <a href="//ryanmulligan.dev/blog/">Ryan Mulligan</a>, <a href="//ishadeed.com/">Ahmad Shadeed</a>, and <a href="//www.joshwcomeau.com/">Josh Comeau</a> use CSS in more stylish, and creative ways.</p>
</li>
</ol>
<h3 id="content-first">Content first<a href="#content-first" title='Permlink for "Content first"'></a></h3>
<p>Here's a better workflow. It has many names: "standards first", "content first", or "progressive enhancement".</p>

<figure class="tall"><img src="https://nuejs.org/img/standards-first.png" alt="Standards first model" loading="lazy"><figcaption>Standards first model</figcaption></figure>
<p>You start with a pure, semantic layout and figure out all the reusable pieces of CSS. At times, especially when building new components, you might want to prototype quickly with inline styling. But that's okay and part of the system. You can clean things up later.</p>
<blockquote>
<p>Clean code is easier to maintain</p>
</blockquote>
<p>There are no "pain points" in clean code, only benefits. This is the system I want to teach to my kids. I want them to understand how web standards work, and where all the trends come from.</p>
<p>Because trends are temporary, but standards are forever.</p>
<h3 id="stay-relevant">Stay relevant<a href="#stay-relevant" title='Permlink for "Stay relevant"'></a></h3>
<p>My guess: It's only a matter of time before Tailwind collapses. The vendor-specific language and the misleading communication cannot hold water very long. The utility soup produced today will eventually turn into a technical debt. The next generation looks back and asks: "You actually wrote <strong>that</strong>?"</p>
<p>Learn to write clean HTML and CSS and stay relevant for years to come.</p>

<img src="https://nuejs.org/img/tw-switch.png" loading="lazy" class="tall">

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

@@ -0,0 +1,250 @@
<!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>command center: Simplicity (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://commandcenter.blogspot.com/2023/12/simplicity.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>command center: Simplicity</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://commandcenter.blogspot.com/2023/12/simplicity.html" title="Lien vers le contenu original">Source originale</a>
<br>
Mis en cache le 2024-02-18
</p>
</nav>
<hr>
<p><span>In May 2009, Google hosted an internal "Design Wizardry" panel, with talks by Jeff Dean, </span><span>Mike Burrows, Paul Haahr, Alfred Spector, Bill Coughran, and myself.</span><span><span> Here is a lightly edited transcript of my talk. Some of the details have aged out, but the themes live on, now perhaps more than ever.</span></span></p>
<p><span>---</span></p>
<p class="p1"><span class="s1"><span><br></span></span></p>
<p class="p1"><span class="s1"><span>Simplicity is better than complexity.</span></span></p>
<p class="p2"><span><span class="s1"></span><br></span></p>
<p class="p1"><span class="s1"><span>Simpler things are easier to understand, easier to build, easier to debug, and easier to maintain. Easier to understand is the most important, because it leads to the others. </span></span><span>Look at the web page for google.com. One text box. Type your query, get useful results. That's brilliantly simple design and a major reason for Google's success. Earlier search engines had much more complicated interfaces. Today they have either mimicked ours, or feel really hard to use.</span></p>
<p class="p2"><span><span class="s1"></span><br></span></p>
<p class="p1"><span class="s1"><span>That's google.com. But what about what's behind it? What about GWS? How you do you invoke it? I looked at the argument list of a running GWS (<a href="https://en.wikipedia.org/wiki/Google_Web_Server" target="_blank">Google Web Server</a>) instance. XX,XXX characters of configuration flags. XXX arguments. A few name backend machines. Some configure backends. Some enable or disable properties. Most of them are probably correct. I guarantee some of them are wrong or at least obsolete.</span></span></p>
<p class="p2"><span><span class="s1"></span><br></span></p>
<p class="p1"><span class="s1"><span>So, here's my question: How can the company that designed google.com be the same company that designed GWS? The answer is that GWS configuration structure was not really designed. It grew organically. Organic growth is not simple; it generates fantastic complexity. Each piece, each change may be simple, but put together the complexity becomes overwhelming.</span></span></p>
<p class="p2"><span><span class="s1"></span><br></span></p>
<p class="p1"><span class="s1"><span>Complexity is multiplicative. In a system, like Google, that is assembled from components, every time you make one part more complex, some of the added complexity is reflected in the other components. It's complexity runaway.</span></span></p>
<p class="p2"><span><span class="s1"></span><br></span></p>
<p class="p1"><span class="s1"><span>It's also endemic.</span></span></p>
<p class="p2"><span><span class="s1"></span><br></span></p>
<p class="p1"><span class="s1"><span>Many years ago, Tom Cargill took a year off from Bell Labs Research to work in development. He joined a group where every subsystem's code was printed in a separate binder and stored on a shelf in each office. Tom discovered that one of those subsystems was almost completely redundant; most of its services were implemented elsewhere. So he spent a few months making it completely redundant. He deleted 15,000 lines of code. When he was done, he removed an entire binder from everybody's shelf. He reduced the complexity of the system. Less code, less to test, less to maintain. His coworkers loved it.</span></span></p>
<p class="p2"><span><span class="s1"></span><br></span></p>
<p class="p1"><span class="s1"><span>But there was a catch. During his performance review, he learned that management had a metric for productivity: lines of code. Tom had negative productivity. In fact, because he was so successful, his entire group had negative productivity. He returned to Research with his tail between his legs.</span></span></p>
<p class="p2"><span><span class="s1"></span><br></span></p>
<p class="p1"><span class="s1"><span>And he learned his lesson: complexity is endemic. Simplicity is not rewarded.</span></span></p>
<p class="p2"><span><span class="s1"></span><br></span></p>
<p class="p1"><span class="s1"><span>You can laugh at that story. We don't do performance review based on lines of code.</span></span></p>
<p class="p2"><span><span class="s1"></span><br></span></p>
<p class="p1"><span class="s1"><span>But we're actually not far off. Who ever got promoted for deleting Google code? We revel in the code we have. It's huge and complex. New hires struggle to grasp it and we spend enormous resources training and mentoring them so they can cope. We pride ourselves in being able to understand it and in the freedom to change it.</span></span></p>
<p class="p2"><span><span class="s1"></span><br></span></p>
<p class="p1"><span class="s1"><span>Google is a democracy; the code is there for all to see, to modify, to improve, to add to. But every time you add something, you add complexity. Add a new library, you add complexity. Add a new storage wrapper, you add complexity. Add an option to a subsystem, you complicate the configuration. And when you complicate something central, such as a networking library, you complicate everything.</span></span></p>
<p class="p2"><span><span class="s1"></span><br></span></p>
<p class="p1"><span class="s1"><span>Complexity just happens and its costs are literally exponential.</span></span></p>
<p class="p2"><span><span class="s1"></span><br></span></p>
<p class="p1"><span class="s1"><span>On the other hand, simplicity takes work—but it's all up front. Simplicity is very hard to design, but it's easier to build and much easier to maintain. By avoiding complexity, simplicity's benefits are exponential.</span></span></p>
<p class="p2"><span><span class="s1"></span><br></span></p>
<p class="p1"><span class="s1"><span>Pardon the solipsism but look at the query logging system. It's far from perfect but it was designed to be—and still is—the only system at Google that solves the particular, central problem it was designed to solve. Because it is the only one, it guarantees stability, security, uniformity of use, and all the economies of scale. There is no way Google would be where it is today if every team rolled out its own logging infrastructure.</span></span></p>
<p class="p2"><span><span class="s1"></span><br></span></p>
<p class="p1"><span class="s1"><span>But the lesson didn't spread. Teams are constantly proposing new storage systems, new workflow managers, new libraries, new infrastructure.</span></span></p>
<p class="p2"><span><span class="s1"></span><br></span></p>
<p class="p1"><span class="s1"><span>All that duplication and proliferation is far too complex and it is killing us because the complexity is slowing us down.</span></span></p>
<p class="p2"><span><span class="s1"></span><br></span></p>
<p class="p1"><span class="s1"><span>We have a number of engineering principles at Google. Make code readable. Make things testable. Don't piss off the SREs. Make things fast.</span></span></p>
<p class="p2"><span><span class="s1"></span><br></span></p>
<p class="p1"><span class="s1"><span>Simplicity has never been on that list. But here's the thing: Simplicity is more important than any of them. Simpler designs are more readable. Simpler code is easier to test. Simpler systems are easier to explain to the SREs, and easier to fix when they fail.</span></span></p>
<p class="p2"><span><span class="s1"></span><br></span></p>
<p class="p1"><span class="s1"><span>Plus, simpler systems run faster.</span></span></p>
<p class="p2"><span><span class="s1"></span><br></span></p>
<p class="p1"><span class="s1"><span>Notice I said systems there, not code. Sometimes—not always—to make code fast you need to complicate it; that can be unavoidable. But complex systems are NEVER fast—they have more pieces and their interactions are too poorly understood to make them fast. Complexity generates inefficiency.</span></span></p>
<p class="p2"><span><span class="s1"></span><br></span></p>
<p class="p1"><span class="s1"><span>Simplicity is even more important than performance. Because of the multiplicative effects of complexity, getting 2% performance improvement by adding 2% complexity—or 1% or maybe even .1%—isn't worth it.</span></span></p>
<p class="p2"><span><span class="s1"></span><br></span></p>
<p class="p1"><span class="s1"><span>But hold on! What about our Utilization Code Red?</span></span></p>
<p class="p2"><span><span class="s1"></span><br></span></p>
<p class="p1"><span class="s1"><span>We don't have utilization problems because our systems are too slow. We have utilization problems because our systems are too complex. We don't understand how they perform, individually or together. We don't know how to characterize their interactions.</span></span></p>
<p class="p2"><span><span class="s1"></span><br></span></p>
<p class="p1"><span class="s1"><span>The app writers don't fully understand the infrastructure.</span></span></p>
<p class="p2"><span><span class="s1"></span><br></span></p>
<p class="p1"><span class="s1"><span>The infrastructure writers don't fully understand the networks.</span></span></p>
<p class="p2"><span><span class="s1"></span><br></span></p>
<p class="p1"><span class="s1"><span>Or the apps for that matter. And so on and so on.</span></span></p>
<p class="p2"><span><span class="s1"></span><br></span></p>
<p class="p1"><span class="s1"><span>To compensate, everyone overprovisions and adds zillions of configuration options and adjustments. That makes everything even harder to understand.</span></span></p>
<p class="p2"><span><span class="s1"></span><br></span></p>
<p class="p1"><span class="s1"><span>Products manage to launch only by building walls around their products to isolate them from the complexity—which just adds more complexity.</span></span></p>
<p class="p2"><span><span class="s1"></span><br></span></p>
<p class="p1"><span class="s1"><span>It's a vicious cycle.</span></span></p>
<p class="p2"><span><span class="s1"></span><br></span></p>
<p class="p1"><span class="s1"><span>So think hard about what you're working on. Can it be simpler? Do you really need that feature? Can you make something better by simplifying, deleting, combining, or sharing? Sit down with the groups you depend on and understand how you can combine forces with them to design a simpler, shared architecture that doesn't involve defending against each other.</span></span></p>
<p class="p2"><span><span class="s1"></span><br></span></p>
<p class="p1"><span class="s1"><span>Learn about the systems that already exist, and build on them rather than around them. If an existing system doesn't do what you want, maybe the problem is in the design of your system, not that one.</span></span></p>
<p class="p2"><span><span class="s1"></span><br></span></p>
<p class="p1"><span class="s1"><span>If you do build a new component, make sure it's of general utility. Don't build infrastructure that solves only the problems of your own team.</span></span></p>
<p class="p2"><span><span class="s1"></span><br></span></p>
<p class="p1"><span class="s1"><span>It's easy to build complexity. In the rush to launch, it's quicker and easier to code than to redesign. But the costs accumulate and you lose in the long run.</span></span></p>
<p class="p2"><span><span class="s1"></span><br></span></p>
<p class="p1"><span class="s1"><span>The code repository contains 50% more lines of code than it did a year ago. Where will we be in another year? In 5 years?</span></span></p>
<p class="p2"><span><span class="s1"></span><br></span></p>
<p class="p1"><span class="s1"><span>If we don't bring the complexity under control, one day it won't be a Utilization Code Red. Things will get so complex, so slow, they'll just grind to a halt. That's called a Code Black.</span></span></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>

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


+ 450
- 0
cache/2024/d0ffe1891c332b6fc6e7d7826d8489da/index.html View File

@@ -0,0 +1,450 @@
<!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>Naming Variables In CSS (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://jwdallas.com/posts/namingcssvariables/">

<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>Naming Variables In CSS</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://jwdallas.com/posts/namingcssvariables/" title="Lien vers le contenu original">Source originale</a>
<br>
Mis en cache le 2024-02-18
</p>
</nav>
<hr>
<p>“Naming things is hard” goes the software engineering axiom and CSS is no exception. Here are some collected thoughts related to naming <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/--*">CSS Custom Properties</a>. I’m going to use use the terms “variable” and “custom property” interchangeably since they are effectively the same thing for the purposes of what to call them.</p>
<p><em>Disclaimer: What follows is not gospel. CSS to me is a very poetic language, there are so many different ways to express the same concepts. I like these conventions but do not consider them the one correct way to name variables in CSS. If you disagree with any of my points below, I would love to learn from your perspective.</em></p>
<h2 id="casing">Casing</h2>
<p>In naming variables, the first thing to talk about is what sort of casing to use. The industry seems have settled on kebab-casing (which makes sense) but I think it’s worth considering an alternative.</p>
<h3 id="maybe-camelcase-isnt-so-bad">Maybe camelCase isn’t so bad</h3>
<p>You might be surprised to learn that many of the native values defined within CSS do not use kebab-casing. For example, <code>currentColor</code> and all of the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/named-color">named colors</a> (<code>cadetBlue</code>, <code>rebeccaPurple</code>, <code>antiqueWhite</code>, etc).</p>
<h3 id="consider-mixing-kebab-casing-with-camelcasing">Consider mixing kebab-casing with camelCasing</h3>
<p>We can use camelCasing mixed with kebab-casing to create variable names that are structurally consistent. The idea is to use hyphens to separate value type and namespace from variable name and then camelCase within each segment. Essentially: <code>namespaceName-valueType-variableName</code>. Let’s call this <strong>triptych notation</strong>. In my opinion, this convention makes it clearer at a glance what is the actual name of the variable and what is the metadata encoded in the name.</p>
<pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>:root</span><span> {</span></span>
<span class="line"><span> /* Harder to scan: */</span></span>
<span class="line"><span> --system-control-accent-color</span><span>: </span><span>blue</span><span>;</span></span>
<span class="line"><span> --system-focus-ring-color</span><span>: </span><span>cadetBlue</span><span>;</span></span>
<span class="line"><span> --system-label-color-quaternary</span><span>: </span><span>lightGray</span><span>;</span></span>
<span class="line"><span> --system-heading-title-font-size</span><span>: </span><span>1.5rem</span><span>;</span></span>
<span class="line"><span> --system-subheading-font-size</span><span>: </span><span>1.2rem</span><span>;</span></span>
<span class="line"><span> --system-caption-font-size</span><span>: </span><span>0.65rem</span><span>;</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span> /* Easier to scan: */</span></span>
<span class="line"><span> --system-color-controlAccent</span><span>: </span><span>blue</span><span>;</span></span>
<span class="line"><span> --system-color-focusRing</span><span>: </span><span>cadetBlue</span><span>;</span></span>
<span class="line"><span> --system-color-labelQuaternary</span><span>: </span><span>lightGray</span><span>;</span></span>
<span class="line"><span> --system-fontSize-headingTitle</span><span>: </span><span>1.5rem</span><span>;</span></span>
<span class="line"><span> --system-fontSize-subheading</span><span>: </span><span>1.2rem</span><span>;</span></span>
<span class="line"><span> --system-fontSize-caption</span><span>: </span><span>0.65rem</span><span>;</span></span>
<span class="line"><span>}</span></span></code></pre>
<p>With triptych notation, camelCase is used to limit the number of hyphens. This allows the middle segment to consistently be the value type and the last segment to consistently be the specific name of the variable. In my opinion, this consistent placement of hyphens makes custom properties easier to read quickly.</p>
<h2 id="namespacing">Namespacing</h2>
<p>The example above has variable names that are prefixed with ‘system’—short for ‘design system’. This is called namespacing. Namespaced variable names can help avoid collisions when CSS is shared by multiple projects. In other words, they help to avoid situations where a developer outside of your project accidentally defines a variable with the same name. Another benefit is that namespacing provides a hint in the web inspector as to which project defined the custom property.</p>
<p>Namespacing your variable names can be important for top level global variable names but I’d argue this type of name scoping is typically <em>not</em> neccesary or useful when a variable is defined below the top level. This is because CSS handles that for you. A custom property is always scoped to the selector in which the property is defined. If you define a custom property with a CSS selector for a custom element called <code>quiz-library</code> that custom property will only exist within DOM nodes that match <code>quiz-library</code> and their children.</p>
<pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>:root</span><span> {</span></span>
<span class="line"><span> /* This variable is defined at the root making it shared</span></span>
<span class="line"><span> globally so having a namespace is useful. */</span></span>
<span class="line"><span> --system-color-labelPrimary</span><span>: </span><span>#000</span><span>;</span></span>
<span class="line"><span>}</span></span>
<span class="line"></span>
<span class="line"><span>quiz-library</span><span> {</span></span>
<span class="line"><span> /* This variable is defined within quiz-library so it’s not</span></span>
<span class="line"><span> shared globally. A namespace of "quizLibrary" would be</span></span>
<span class="line"><span> redundant because the variable is only available within</span></span>
<span class="line"><span> quiz-library elements and their descendents. */</span></span>
<span class="line"><span> --color-questionTitle</span><span>: </span><span>var</span><span>(</span><span>--system-color-labelPrimary</span><span>);</span></span>
<span class="line"><span>}</span></span></code></pre>
<h2 id="value-typing">Value typing</h2>
<p>The examples above include the type of the value (‘color’, ‘fontSize’, etc) in the custom property name. Consider including value type information in variable names so that maintainers of the code can have a sense of what kind of value the variable holds. This is often referred to as <a href="https://en.wikipedia.org/wiki/Hungarian_notation">Hungarian notation</a>.</p>
<pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>button</span><span> {</span></span>
<span class="line"><span> /* Did they set a font family definition to a font size? */</span></span>
<span class="line"><span> font-size</span><span>: </span><span>var</span><span>(</span><span>--system-elephant</span><span>);</span></span>
<span class="line"><span>}</span></span>
<span class="line"></span>
<span class="line"><span>button</span><span> {</span></span>
<span class="line"><span> /* Clear now that the variable sets a font size. */</span></span>
<span class="line"><span> font-size</span><span>: </span><span>var</span><span>(</span><span>--system-fontSize-elephant</span><span>);</span></span>
<span class="line"><span>}</span></span></code></pre>
<h2 id="names-that-are-descriptive">Names that are descriptive</h2>
<p>There are two fundamental categories of variable names in CSS. Consider these two variables:</p>
<ol>
<li><code>--color-icyBlue</code> (value-based)</li>
<li><code>--color-accent</code> (usage-based)</li>
</ol>
<p>One of them is labeled as a constant—by name, “Icy Blue” should never hold anything other than a blue color value. The other is more dynamic, the specific color held by “Accent” could be expected to change depending on where it is used; for example, which project the variable is used within.</p>
<p>People call these categories lots of different names. I’m going to to call them <strong>value-based</strong>: names that describe a value, and <strong>usage-based</strong>: names that describe a use.</p>
<h3 id="where-to-use-value-based-naming">Where to use value-based naming</h3>
<p>Variables with value-based names can be useful for restricting the number of values in your interface. As an example, it’s good design to limit your interface to a small set of colors. If every part of your UI uses a slightly different shade of gray, your design will look inconsistent and unconsidered. Requiring every use of color in your interface to be a variable allows you to limit your colors to the set defined as variables. The number of font sizes, font weights, animation durations, panel elevations (defined by the presentation of their shadows) can all be useful things to limit.</p>
<pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>/* Value-based variables at the global level */</span></span>
<span class="line"><span>:root</span><span> {</span></span>
<span class="line"><span> /* Colors */</span></span>
<span class="line"><span> --system-color-bondiBlue</span><span>: </span><span>rgb</span><span>(</span><span>0</span><span> 58</span><span> 71</span><span>);</span></span>
<span class="line"><span> --system-color-canaryYellow</span><span>: </span><span>rgb</span><span>(</span><span>255</span><span> 239</span><span> 0</span><span>);</span></span>
<span class="line"><span> --system-color-caribbeanGreen</span><span>: </span><span>rgb</span><span>(</span><span>0</span><span> 204</span><span> 153</span><span>);</span></span>
<span class="line"><span> </span></span>
<span class="line"><span> /* Font Sizes */</span></span>
<span class="line"><span> --system-fontSize-jumbo</span><span>: </span><span>3.052rem</span><span>;</span></span>
<span class="line"><span> --system-fontSize-large</span><span>: </span><span>1.563rem</span><span>;</span></span>
<span class="line"><span> --system-fontSize-small</span><span>: </span><span>0.8rem</span><span>;</span></span>
<span class="line"><span> </span></span>
<span class="line"><span> /* Font Weights */</span></span>
<span class="line"><span> --system-fontWeight-bold</span><span>: </span><span>700</span><span>;</span></span>
<span class="line"><span> --system-fontWeight-medium</span><span>: </span><span>400</span><span>;</span></span>
<span class="line"><span> --system-fontWeight-light</span><span>: </span><span>200</span><span>;</span></span>
<span class="line"><span> </span></span>
<span class="line"><span> /* Durations */</span></span>
<span class="line"><span> --system-duration-presto</span><span>: </span><span>60ms</span><span>;</span></span>
<span class="line"><span> --system-duration-allegro</span><span>: </span><span>125ms</span><span>;</span></span>
<span class="line"><span> --system-duration-andante</span><span>: </span><span>500ms</span><span>;</span></span>
<span class="line"></span>
<span class="line"><span> /* Elevation */</span></span>
<span class="line"><span> --system-boxShadow-slightlyRaised</span><span>: </span><span>0</span><span> 1px</span><span> 2px</span><span> 0</span><span> rgb</span><span>(</span><span>0</span><span> 0</span><span> 0</span><span> / </span><span>10%</span><span>);</span></span>
<span class="line"><span> --system-boxShadow-floatingBox</span><span>: </span><span>0</span><span> 0</span><span> 30px</span><span> 0</span><span> rgb</span><span>(</span><span>0</span><span> 0</span><span> 0</span><span> / </span><span>35%</span><span>);</span></span>
<span class="line"><span>}</span></span></code></pre>
<h3 id="color-palettes">Color palettes</h3>
<p>Many design systems name the colors in their color palette with a <a href="https://spectrum.adobe.com/page/color-fundamentals/#Contrast-generated-colors">numeric suffix</a> to indicate contrast with a base background color. The thinking is that it can be useful for consumers of the palette to be able to easily determine if a particular color will pass <a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/Understanding_WCAG/Perceivable/Color_contrast">WCAG requirements for text color contrast</a>. This is a clever idea but in many projects the colors that are used for text (the only ones which matter for WCAG’s contrast requirements) are very limited so naming the entire color palette that way just for a few colors can be overkill. Additionally I’m unconvinced that these numbers actually make it faster to implement contrast safe UIs. The contrast algorithm used by WCAG is <a href="https://typefully.com/u/DanHollick/t/sle13GMW2Brp">likely going to change</a> and there are a number of ways a color could be transformed in a way that would negate the value of the numeric suffix. If you’re going to need to always double-check contrast-ratio in the rendered UI, no time has been saved using these numbers.</p>
<p>That said, these numbers do provide a useful utility of being able to see at a glance whether a color is lighter or darker. Though I feel using words rather than numbers is a nicer more human friendly way to accomplish that. Consider using compound names for color variables. One name that refers to the basic color (“red”, “yellow”, “blue”) and another that acts as a differentiator (“cherry”, “sunflower”, “sky”).</p>
<pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>:root</span><span> {</span></span>
<span class="line"><span> /* Not very human friendly */</span></span>
<span class="line"><span> --system-color-red400</span><span>: </span><span>hsl</span><span>(</span><span>0</span><span> 100%</span><span> 50%</span><span>);</span></span>
<span class="line"><span> --system-color-yellow200</span><span>: </span><span>hsl</span><span>(</span><span>48</span><span> 100%</span><span> 50%</span><span>);</span></span>
<span class="line"><span> --system-color-blue300</span><span>: </span><span>hsl</span><span>(</span><span>200</span><span> 100%</span><span> 50%</span><span>);</span></span>
<span class="line"></span>
<span class="line"><span> /* Friendlier and easier to understand */</span></span>
<span class="line"><span> --system-color-cherryRed</span><span>: </span><span>hsl</span><span>(</span><span>0</span><span> 100%</span><span> 50%</span><span>);</span></span>
<span class="line"><span> --system-color-sunflowerYellow</span><span>: </span><span>hsl</span><span>(</span><span>48</span><span> 100%</span><span> 50%</span><span>);</span></span>
<span class="line"><span> --system-color-skyBlue</span><span>: </span><span>hsl</span><span>(</span><span>200</span><span> 100%</span><span> 50%</span><span>);</span></span>
<span class="line"><span>}</span></span></code></pre>
<p>Keep differentiators analogous to real world things to avoid confusion. Don’t use an abstract name like <code>historyBlue</code> because it would be unclear what that color would look like.</p>
<p>The goal is to get to a unique color name format that can support an endless number of tints, shades, and tones but at glance still be clear from the name what the color probably looks like so someone can see if the color was accidentally used in the wrong spot in the code.</p>
<pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>button.destructive</span><span> {</span></span>
<span class="line"><span> /* There's a UX bug in our code if this color isn't red</span></span>
<span class="line"><span> but the variable name below is somewhat ambiguous. */</span></span>
<span class="line"><span> color</span><span>: </span><span>var</span><span>(</span><span>--system-color-ferrari</span><span>);</span></span>
<span class="line"><span>}</span></span>
<span class="line"></span>
<span class="line"><span>button.destructive</span><span> {</span></span>
<span class="line"><span> /* Clearer now at a glance that a red color was correctly set */</span></span>
<span class="line"><span> color</span><span>: </span><span>var</span><span>(</span><span>--system-color-ferrariRed</span><span>);</span></span>
<span class="line"><span>}</span></span></code></pre>
<p>This naming convention can be expanded to incorporate alpha, though it’s a bit of a stretch. Separately from naming, just as general practice, it’s often better to reach non-opaque color values in UI via layered transformations or usage-based named variables than to put them into the static color palette. That said, when I <em>have</em> needed to write a value-based variable name for a non-opaque value using this convention I’ve put that info at the start of the color name using terms analogous to real world transparency.</p>
<pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>:root</span><span> {</span></span>
<span class="line"><span> --system-color-semitransparentBondiBlue</span><span>: </span><span>rgb</span><span>(</span><span>0</span><span> 58</span><span> 71</span><span> / </span><span>10%</span><span>);</span></span>
<span class="line"><span> --system-color-translucentBondiBlue</span><span>: </span><span>rgb</span><span>(</span><span>0</span><span> 58</span><span> 71</span><span> / </span><span>30%</span><span>);</span></span>
<span class="line"><span> --system-color-frostedBondiBlue</span><span>: </span><span>rgb</span><span>(</span><span>0</span><span> 58</span><span> 71</span><span> / </span><span>70%</span><span>);</span></span>
<span class="line"><span>}</span></span></code></pre>
<h3 id="where-to-use-usage-based-naming">Where to use usage-based naming</h3>
<p>Variable names tied to use provide varying levels of abstraction. Put another way, names can communicate different scopes of capability and utility within the project by describing uses that are more specific or more general. Some are very narrow in use because they describe a very specific thing and some are very wide in use because they describe a general category of things.</p>
<p>For a very contrived example, consider naming the font weight used in a button that submits a registration form. That variable could be named something like <code>--fontWeight-regFormSubmitButton</code> but that’s very specific. Typically all submit buttons look the same way in which case the concept of a ‘submit button font weight’ could be abstracted out into a less specific name like <code>--fontWeight-submitButton</code>. That name is more general and as a result at a higher level abstraction because it doesn’t refer to a specific form anymore.</p>
<p>It often makes sense to combine variables with multiple levels of abstraction in a project. Here is how that could come into play with control tinting:</p>
<pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>:root</span><span> {</span></span>
<span class="line"><span> /* Color palette defined at root */</span></span>
<span class="line"><span> --system-color-bondiBlue</span><span>: </span><span>rgb</span><span>(</span><span>0</span><span> 58</span><span> 71</span><span>);</span></span>
<span class="line"><span> --system-color-canaryYellow</span><span>: </span><span>rgb</span><span>(</span><span>255</span><span> 239</span><span> 0</span><span>);</span></span>
<span class="line"><span>}</span></span>
<span class="line"></span>
<span class="line"><span>body</span><span> {</span></span>
<span class="line"><span> /* Custom property for custom controls */</span></span>
<span class="line"><span> --color-accentColor</span><span>: </span><span>var</span><span>(</span><span>--system-color-bondiBlue</span><span>);</span></span>
<span class="line"><span> /* Reflect it below for native controls */</span></span>
<span class="line"><span> accent-color</span><span>: </span><span>var</span><span>(</span><span>--color-accentColor</span><span>);</span></span>
<span class="line"><span>}</span></span>
<span class="line"></span>
<span class="line"><span>foobar-custom-control</span><span> {</span></span>
<span class="line"><span> /* Define CSS interface allowing the background to be changed */</span></span>
<span class="line"><span> --color-background</span><span>: </span><span>var</span><span>(</span><span>--accentColor</span><span>);</span></span>
<span class="line"><span> /* Implement the above interface */</span></span>
<span class="line"><span> background</span><span>: </span><span>var</span><span>(</span><span>--color-background</span><span>);</span></span>
<span class="line"><span>}</span></span>
<span class="line"></span>
<span class="line"><span>form.tinted</span><span> {</span></span>
<span class="line"><span> /* Define CSS interface for applying tint colors to form */</span></span>
<span class="line"><span> --color-formTint</span><span>: </span><span>var</span><span>(</span><span>--system-color-canaryYellow</span><span>);</span></span>
<span class="line"><span>}</span></span>
<span class="line"></span>
<span class="line"><span>form.tinted</span><span> foobar-custom-control</span><span> {</span></span>
<span class="line"><span> /* Utilize the above interface for foobar-custom-control */</span></span>
<span class="line"><span> --color-background</span><span>: </span><span>var</span><span>(</span><span>--color-formTint</span><span>);</span></span>
<span class="line"><span>}</span></span></code></pre>
<h3 id="dark-mode-is-simpler-with-usage-based-variables">Dark mode is simpler with usage-based variables</h3>
<p>Consider an implementation of dark mode styling without usage-based variables vs one with them. When using only value-based variables, the code is much more repetitive and verbose.</p>
<pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>/* Dark mode WITHOUT usage-based variables… */</span></span>
<span class="line"></span>
<span class="line"><span>:root</span><span> {</span></span>
<span class="line"><span> --system-color-deepBlack</span><span>: </span><span>#333</span><span>;</span></span>
<span class="line"><span> --system-color-offWhite</span><span>: </span><span>#eee</span><span>;</span></span>
<span class="line"><span> --system-color-skyBlue</span><span>: </span><span>lch</span><span>(</span><span>33</span><span> 111</span><span> 231.17</span><span>);</span></span>
<span class="line"><span> --system-color-deepBlue</span><span>: </span><span>lch</span><span>(</span><span>14</span><span> 111</span><span> 231.17</span><span>);</span></span>
<span class="line"><span>}</span></span>
<span class="line"></span>
<span class="line"><span>/* Because the variables above are named in</span></span>
<span class="line"><span> a value-based way we can’t reasonably change</span></span>
<span class="line"><span> their values. Instead we fork our CSS below</span></span>
<span class="line"><span> to use one or the other depending on the</span></span>
<span class="line"><span> root appearance. */</span></span>
<span class="line"></span>
<span class="line"><span>[</span><span>data-appearance</span><span>=</span><span>"light"</span><span>] </span><span>body</span><span> {</span></span>
<span class="line"><span> color</span><span>: </span><span>var</span><span>(</span><span>--system-color-deepBlack</span><span>);</span></span>
<span class="line"><span> background</span><span>: </span><span>var</span><span>(</span><span>--system-color-offWhite</span><span>);</span></span>
<span class="line"><span>}</span></span>
<span class="line"></span>
<span class="line"><span>[</span><span>data-appearance</span><span>=</span><span>"dark"</span><span>] </span><span>body</span><span> {</span></span>
<span class="line"><span> color</span><span>: </span><span>var</span><span>(</span><span>--system-color-offWhite</span><span>);</span></span>
<span class="line"><span> background</span><span>: </span><span>var</span><span>(</span><span>--system-color-deepBlack</span><span>);</span></span>
<span class="line"><span>}</span></span>
<span class="line"></span>
<span class="line"><span>[</span><span>data-appearance</span><span>=</span><span>"light"</span><span>] </span><span>a</span><span> {</span></span>
<span class="line"><span> color</span><span>: </span><span>var</span><span>(</span><span>--system-color-deepBlue</span><span>);</span></span>
<span class="line"><span>}</span></span>
<span class="line"></span>
<span class="line"><span>[</span><span>data-appearance</span><span>=</span><span>"dark"</span><span>] </span><span>a</span><span> {</span></span>
<span class="line"><span> color</span><span>: </span><span>var</span><span>(</span><span>--system-color-skyBlue</span><span>);</span></span>
<span class="line"><span>}</span></span></code></pre>
<p>With usage-based names you can define color variables as interface concepts that have understood meanings beyond individual UI pieces allowing for those values to be externally changed for dark mode without needing to maintain separate light/dark CSS for each new piece of UI.</p>
<pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>/* Dark mode WITH usage-based variables… */</span></span>
<span class="line"></span>
<span class="line"><span>[</span><span>data-appearance</span><span>=</span><span>"light"</span><span>] {</span></span>
<span class="line"><span> --system-color-textPrimary</span><span>: </span><span>#333</span><span>;</span></span>
<span class="line"><span> --system-color-fillPrimary</span><span>: </span><span>#eee</span><span>;</span></span>
<span class="line"><span> --system-color-link</span><span>: </span><span>lch</span><span>(</span><span>14</span><span> 111</span><span> 231.17</span><span>);</span></span>
<span class="line"><span>}</span></span>
<span class="line"><span>[</span><span>data-appearance</span><span>=</span><span>"dark"</span><span>] {</span></span>
<span class="line"><span> --system-color-textPrimary</span><span>: </span><span>#eee</span><span>;</span></span>
<span class="line"><span> --system-color-fillPrimary</span><span>: </span><span>#333</span><span>;</span></span>
<span class="line"><span> --system-color-link</span><span>: </span><span>lch</span><span>(</span><span>33</span><span> 111</span><span> 231.17</span><span>);</span></span>
<span class="line"><span>}</span></span>
<span class="line"></span>
<span class="line"><span>/* With usage-specific variable names we can</span></span>
<span class="line"><span> change the values for the uses they describe</span></span>
<span class="line"><span> at a very high level of abstraction allowing</span></span>
<span class="line"><span> the lower level code that uses the variables</span></span>
<span class="line"><span> not to need to understand the current</span></span>
<span class="line"><span> appearance mode. */</span></span>
<span class="line"></span>
<span class="line"><span>body</span><span> {</span></span>
<span class="line"><span> color</span><span>: </span><span>var</span><span>(</span><span>--system-color-textPrimary</span><span>);</span></span>
<span class="line"><span> background</span><span>: </span><span>var</span><span>(</span><span>--system-color-fillPrimary</span><span>);</span></span>
<span class="line"><span>}</span></span>
<span class="line"></span>
<span class="line"><span>a</span><span> {</span></span>
<span class="line"><span> color</span><span>: </span><span>var</span><span>(</span><span>--system-color-link</span><span>);</span></span>
<span class="line"><span>}</span></span></code></pre>
<h3 id="levels-of-hierarchy-within-usage-based-variables">Levels of hierarchy within usage-based variables</h3>
<p>Any time you have a design that references the same value across mulitple pieces of UI, I’d suggest that is an opportunity for abstracting that value into a name that better describes the intention of the value in the design.</p>
<p>For example, if the background color of your sidebar and your info panels are both <code>#eee</code>, relative to the <code>#fff</code> of your main background, perhaps your intent for that color from the design perspective is to convey to the user that UI with that background is of a secondary nature.</p>
<pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>:root</span><span> {</span></span>
<span class="line"><span> --system-color-backgroundPrimary</span><span>: </span><span>#fff</span><span>;</span></span>
<span class="line"><span> --system-color-backgroundSecondary</span><span>: </span><span>#eee</span><span>;</span></span>
<span class="line"><span>}</span></span></code></pre>
<p>The utility of a usage-based name comes in how it guides a developer or designer in its use. Be careful to avoid using names the are too generic. For example, <code>--system-color-primary</code> is too open-ended in meaning making it unclear where it should be used.</p>
<pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>/* Do not do this */</span></span>
<span class="line"></span>
<span class="line"><span>:root</span><span> {</span></span>
<span class="line"><span> /* What is this for? */</span></span>
<span class="line"><span> --system-color-box</span><span>: </span><span>var</span><span>(</span><span>--system-color-neonBlue</span><span>);</span></span>
<span class="line"><span>}</span></span>
<span class="line"></span>
<span class="line"><span>:is</span><span>(</span><span>a</span><span>, </span><span>button</span><span>, </span><span>input</span><span>)</span><span>:focus-visible</span><span> {</span></span>
<span class="line"><span> /* Was this variable used correctly? */</span></span>
<span class="line"><span> background</span><span>: </span><span>var</span><span>(</span><span>--system-color-box</span><span>);</span></span>
<span class="line"><span>}</span></span></code></pre>
<pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>/* Do this instead */</span></span>
<span class="line"></span>
<span class="line"><span>:root</span><span> {</span></span>
<span class="line"><span> /* Clear what this is for */</span></span>
<span class="line"><span> --system-color-focusRing</span><span>: </span><span>var</span><span>(</span><span>--system-color-neonBlue</span><span>);</span></span>
<span class="line"><span>}</span></span>
<span class="line"></span>
<span class="line"><span>:is</span><span>(</span><span>a</span><span>, </span><span>button</span><span>, </span><span>input</span><span>)</span><span>:focus-visible</span><span> {</span></span>
<span class="line"><span> /* Clear it was used correctly */</span></span>
<span class="line"><span> outline-color</span><span>: </span><span>var</span><span>(</span><span>--system-color-focusRing</span><span>);</span></span>
<span class="line"><span>}</span></span></code></pre>
<p>Be careful to avoid using names that are too specific. For example, <code>--system-color-mainToolbarBackground</code> could only be used in one spot which makes the use of a variable superfluous.</p>
<pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>/* Do not do this */</span></span>
<span class="line"></span>
<span class="line"><span>:root</span><span> {</span></span>
<span class="line"><span> /* This can only be used in one place meaning its</span></span>
<span class="line"><span> existence needlessly adds complexity to the project */</span></span>
<span class="line"><span> --system-color-mainToolbarBackground</span><span>: </span><span>#eee</span><span>;</span></span>
<span class="line"><span>}</span></span>
<span class="line"></span>
<span class="line"><span>main</span><span> .toolbar</span><span> {</span></span>
<span class="line"><span> background</span><span>: </span><span>var</span><span>(</span><span>--system-color-mainToolbarBackground</span><span>);</span></span>
<span class="line"><span>}</span></span></code></pre>
<pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>/* Do this instead */</span></span>
<span class="line"></span>
<span class="line"><span>:root</span><span> {</span></span>
<span class="line"><span> /* This name is general enough in scope that it</span></span>
<span class="line"><span> can be used across all UI so it is useful at</span></span>
<span class="line"><span> the global level. */</span></span>
<span class="line"><span> --system-color-backgroundSecondary</span><span>: </span><span>#eee</span><span>;</span></span>
<span class="line"><span>}</span></span>
<span class="line"></span>
<span class="line"><span>main</span><span> .toolbar</span><span> {</span></span>
<span class="line"><span> background</span><span>: </span><span>var</span><span>(</span><span>--system-color-backgroundSecondary</span><span>);</span></span>
<span class="line"><span>}</span></span></code></pre>
<h2 id="wrapping-up">Wrapping up</h2>
<p>There’s a lot more to say on this topic but I’m going to end here for now. Please feel free to reach out, there are links in the footer. I’d love to continue the discussion!</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>

+ 283
- 0
cache/2024/d0ffe1891c332b6fc6e7d7826d8489da/index.md View File

@@ -0,0 +1,283 @@
title: Naming Variables In CSS
url: https://jwdallas.com/posts/namingcssvariables/
hash_url: d0ffe1891c332b6fc6e7d7826d8489da
archive_date: 2024-02-18
og_image:
description: Some collected thoughts around how to name variables in CSS. Ideas, conventions, and some do's and don't for consideration.
favicon: https://jwdallas.com/icons/favicon.ico
language: en_US

<p>“Naming things is hard” goes the software engineering axiom and CSS is no exception. Here are some collected thoughts related to naming <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/--*">CSS Custom Properties</a>. I’m going to use use the terms “variable” and “custom property” interchangeably since they are effectively the same thing for the purposes of what to call them.</p>
<p><em>Disclaimer: What follows is not gospel. CSS to me is a very poetic language, there are so many different ways to express the same concepts. I like these conventions but do not consider them the one correct way to name variables in CSS. If you disagree with any of my points below, I would love to learn from your perspective.</em></p>
<h2 id="casing">Casing</h2>
<p>In naming variables, the first thing to talk about is what sort of casing to use. The industry seems have settled on kebab-casing (which makes sense) but I think it’s worth considering an alternative.</p>
<h3 id="maybe-camelcase-isnt-so-bad">Maybe camelCase isn’t so bad</h3>
<p>You might be surprised to learn that many of the native values defined within CSS do not use kebab-casing. For example, <code>currentColor</code> and all of the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/named-color">named colors</a> (<code>cadetBlue</code>, <code>rebeccaPurple</code>, <code>antiqueWhite</code>, etc).</p>
<h3 id="consider-mixing-kebab-casing-with-camelcasing">Consider mixing kebab-casing with camelCasing</h3>
<p>We can use camelCasing mixed with kebab-casing to create variable names that are structurally consistent. The idea is to use hyphens to separate value type and namespace from variable name and then camelCase within each segment. Essentially: <code>namespaceName-valueType-variableName</code>. Let’s call this <strong>triptych notation</strong>. In my opinion, this convention makes it clearer at a glance what is the actual name of the variable and what is the metadata encoded in the name.</p>
<pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>:root</span><span> {</span></span>
<span class="line"><span> /* Harder to scan: */</span></span>
<span class="line"><span> --system-control-accent-color</span><span>: </span><span>blue</span><span>;</span></span>
<span class="line"><span> --system-focus-ring-color</span><span>: </span><span>cadetBlue</span><span>;</span></span>
<span class="line"><span> --system-label-color-quaternary</span><span>: </span><span>lightGray</span><span>;</span></span>
<span class="line"><span> --system-heading-title-font-size</span><span>: </span><span>1.5rem</span><span>;</span></span>
<span class="line"><span> --system-subheading-font-size</span><span>: </span><span>1.2rem</span><span>;</span></span>
<span class="line"><span> --system-caption-font-size</span><span>: </span><span>0.65rem</span><span>;</span></span>
<span class="line"></span>
<span class="line"></span>
<span class="line"><span> /* Easier to scan: */</span></span>
<span class="line"><span> --system-color-controlAccent</span><span>: </span><span>blue</span><span>;</span></span>
<span class="line"><span> --system-color-focusRing</span><span>: </span><span>cadetBlue</span><span>;</span></span>
<span class="line"><span> --system-color-labelQuaternary</span><span>: </span><span>lightGray</span><span>;</span></span>
<span class="line"><span> --system-fontSize-headingTitle</span><span>: </span><span>1.5rem</span><span>;</span></span>
<span class="line"><span> --system-fontSize-subheading</span><span>: </span><span>1.2rem</span><span>;</span></span>
<span class="line"><span> --system-fontSize-caption</span><span>: </span><span>0.65rem</span><span>;</span></span>
<span class="line"><span>}</span></span></code></pre>
<p>With triptych notation, camelCase is used to limit the number of hyphens. This allows the middle segment to consistently be the value type and the last segment to consistently be the specific name of the variable. In my opinion, this consistent placement of hyphens makes custom properties easier to read quickly.</p>
<h2 id="namespacing">Namespacing</h2>
<p>The example above has variable names that are prefixed with ‘system’—short for ‘design system’. This is called namespacing. Namespaced variable names can help avoid collisions when CSS is shared by multiple projects. In other words, they help to avoid situations where a developer outside of your project accidentally defines a variable with the same name. Another benefit is that namespacing provides a hint in the web inspector as to which project defined the custom property.</p>
<p>Namespacing your variable names can be important for top level global variable names but I’d argue this type of name scoping is typically <em>not</em> neccesary or useful when a variable is defined below the top level. This is because CSS handles that for you. A custom property is always scoped to the selector in which the property is defined. If you define a custom property with a CSS selector for a custom element called <code>quiz-library</code> that custom property will only exist within DOM nodes that match <code>quiz-library</code> and their children.</p>
<pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>:root</span><span> {</span></span>
<span class="line"><span> /* This variable is defined at the root making it shared</span></span>
<span class="line"><span> globally so having a namespace is useful. */</span></span>
<span class="line"><span> --system-color-labelPrimary</span><span>: </span><span>#000</span><span>;</span></span>
<span class="line"><span>}</span></span>
<span class="line"></span>
<span class="line"><span>quiz-library</span><span> {</span></span>
<span class="line"><span> /* This variable is defined within quiz-library so it’s not</span></span>
<span class="line"><span> shared globally. A namespace of "quizLibrary" would be</span></span>
<span class="line"><span> redundant because the variable is only available within</span></span>
<span class="line"><span> quiz-library elements and their descendents. */</span></span>
<span class="line"><span> --color-questionTitle</span><span>: </span><span>var</span><span>(</span><span>--system-color-labelPrimary</span><span>);</span></span>
<span class="line"><span>}</span></span></code></pre>
<h2 id="value-typing">Value typing</h2>
<p>The examples above include the type of the value (‘color’, ‘fontSize’, etc) in the custom property name. Consider including value type information in variable names so that maintainers of the code can have a sense of what kind of value the variable holds. This is often referred to as <a href="https://en.wikipedia.org/wiki/Hungarian_notation">Hungarian notation</a>.</p>
<pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>button</span><span> {</span></span>
<span class="line"><span> /* Did they set a font family definition to a font size? */</span></span>
<span class="line"><span> font-size</span><span>: </span><span>var</span><span>(</span><span>--system-elephant</span><span>);</span></span>
<span class="line"><span>}</span></span>
<span class="line"></span>
<span class="line"><span>button</span><span> {</span></span>
<span class="line"><span> /* Clear now that the variable sets a font size. */</span></span>
<span class="line"><span> font-size</span><span>: </span><span>var</span><span>(</span><span>--system-fontSize-elephant</span><span>);</span></span>
<span class="line"><span>}</span></span></code></pre>
<h2 id="names-that-are-descriptive">Names that are descriptive</h2>
<p>There are two fundamental categories of variable names in CSS. Consider these two variables:</p>
<ol>
<li><code>--color-icyBlue</code> (value-based)</li>
<li><code>--color-accent</code> (usage-based)</li>
</ol>
<p>One of them is labeled as a constant—by name, “Icy Blue” should never hold anything other than a blue color value. The other is more dynamic, the specific color held by “Accent” could be expected to change depending on where it is used; for example, which project the variable is used within.</p>
<p>People call these categories lots of different names. I’m going to to call them <strong>value-based</strong>: names that describe a value, and <strong>usage-based</strong>: names that describe a use.</p>
<h3 id="where-to-use-value-based-naming">Where to use value-based naming</h3>
<p>Variables with value-based names can be useful for restricting the number of values in your interface. As an example, it’s good design to limit your interface to a small set of colors. If every part of your UI uses a slightly different shade of gray, your design will look inconsistent and unconsidered. Requiring every use of color in your interface to be a variable allows you to limit your colors to the set defined as variables. The number of font sizes, font weights, animation durations, panel elevations (defined by the presentation of their shadows) can all be useful things to limit.</p>
<pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>/* Value-based variables at the global level */</span></span>
<span class="line"><span>:root</span><span> {</span></span>
<span class="line"><span> /* Colors */</span></span>
<span class="line"><span> --system-color-bondiBlue</span><span>: </span><span>rgb</span><span>(</span><span>0</span><span> 58</span><span> 71</span><span>);</span></span>
<span class="line"><span> --system-color-canaryYellow</span><span>: </span><span>rgb</span><span>(</span><span>255</span><span> 239</span><span> 0</span><span>);</span></span>
<span class="line"><span> --system-color-caribbeanGreen</span><span>: </span><span>rgb</span><span>(</span><span>0</span><span> 204</span><span> 153</span><span>);</span></span>
<span class="line"><span> </span></span>
<span class="line"><span> /* Font Sizes */</span></span>
<span class="line"><span> --system-fontSize-jumbo</span><span>: </span><span>3.052rem</span><span>;</span></span>
<span class="line"><span> --system-fontSize-large</span><span>: </span><span>1.563rem</span><span>;</span></span>
<span class="line"><span> --system-fontSize-small</span><span>: </span><span>0.8rem</span><span>;</span></span>
<span class="line"><span> </span></span>
<span class="line"><span> /* Font Weights */</span></span>
<span class="line"><span> --system-fontWeight-bold</span><span>: </span><span>700</span><span>;</span></span>
<span class="line"><span> --system-fontWeight-medium</span><span>: </span><span>400</span><span>;</span></span>
<span class="line"><span> --system-fontWeight-light</span><span>: </span><span>200</span><span>;</span></span>
<span class="line"><span> </span></span>
<span class="line"><span> /* Durations */</span></span>
<span class="line"><span> --system-duration-presto</span><span>: </span><span>60ms</span><span>;</span></span>
<span class="line"><span> --system-duration-allegro</span><span>: </span><span>125ms</span><span>;</span></span>
<span class="line"><span> --system-duration-andante</span><span>: </span><span>500ms</span><span>;</span></span>
<span class="line"></span>
<span class="line"><span> /* Elevation */</span></span>
<span class="line"><span> --system-boxShadow-slightlyRaised</span><span>: </span><span>0</span><span> 1px</span><span> 2px</span><span> 0</span><span> rgb</span><span>(</span><span>0</span><span> 0</span><span> 0</span><span> / </span><span>10%</span><span>);</span></span>
<span class="line"><span> --system-boxShadow-floatingBox</span><span>: </span><span>0</span><span> 0</span><span> 30px</span><span> 0</span><span> rgb</span><span>(</span><span>0</span><span> 0</span><span> 0</span><span> / </span><span>35%</span><span>);</span></span>
<span class="line"><span>}</span></span></code></pre>
<h3 id="color-palettes">Color palettes</h3>
<p>Many design systems name the colors in their color palette with a <a href="https://spectrum.adobe.com/page/color-fundamentals/#Contrast-generated-colors">numeric suffix</a> to indicate contrast with a base background color. The thinking is that it can be useful for consumers of the palette to be able to easily determine if a particular color will pass <a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/Understanding_WCAG/Perceivable/Color_contrast">WCAG requirements for text color contrast</a>. This is a clever idea but in many projects the colors that are used for text (the only ones which matter for WCAG’s contrast requirements) are very limited so naming the entire color palette that way just for a few colors can be overkill. Additionally I’m unconvinced that these numbers actually make it faster to implement contrast safe UIs. The contrast algorithm used by WCAG is <a href="https://typefully.com/u/DanHollick/t/sle13GMW2Brp">likely going to change</a> and there are a number of ways a color could be transformed in a way that would negate the value of the numeric suffix. If you’re going to need to always double-check contrast-ratio in the rendered UI, no time has been saved using these numbers.</p>
<p>That said, these numbers do provide a useful utility of being able to see at a glance whether a color is lighter or darker. Though I feel using words rather than numbers is a nicer more human friendly way to accomplish that. Consider using compound names for color variables. One name that refers to the basic color (“red”, “yellow”, “blue”) and another that acts as a differentiator (“cherry”, “sunflower”, “sky”).</p>
<pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>:root</span><span> {</span></span>
<span class="line"><span> /* Not very human friendly */</span></span>
<span class="line"><span> --system-color-red400</span><span>: </span><span>hsl</span><span>(</span><span>0</span><span> 100%</span><span> 50%</span><span>);</span></span>
<span class="line"><span> --system-color-yellow200</span><span>: </span><span>hsl</span><span>(</span><span>48</span><span> 100%</span><span> 50%</span><span>);</span></span>
<span class="line"><span> --system-color-blue300</span><span>: </span><span>hsl</span><span>(</span><span>200</span><span> 100%</span><span> 50%</span><span>);</span></span>
<span class="line"></span>
<span class="line"><span> /* Friendlier and easier to understand */</span></span>
<span class="line"><span> --system-color-cherryRed</span><span>: </span><span>hsl</span><span>(</span><span>0</span><span> 100%</span><span> 50%</span><span>);</span></span>
<span class="line"><span> --system-color-sunflowerYellow</span><span>: </span><span>hsl</span><span>(</span><span>48</span><span> 100%</span><span> 50%</span><span>);</span></span>
<span class="line"><span> --system-color-skyBlue</span><span>: </span><span>hsl</span><span>(</span><span>200</span><span> 100%</span><span> 50%</span><span>);</span></span>
<span class="line"><span>}</span></span></code></pre>
<p>Keep differentiators analogous to real world things to avoid confusion. Don’t use an abstract name like <code>historyBlue</code> because it would be unclear what that color would look like.</p>
<p>The goal is to get to a unique color name format that can support an endless number of tints, shades, and tones but at glance still be clear from the name what the color probably looks like so someone can see if the color was accidentally used in the wrong spot in the code.</p>
<pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>button.destructive</span><span> {</span></span>
<span class="line"><span> /* There's a UX bug in our code if this color isn't red</span></span>
<span class="line"><span> but the variable name below is somewhat ambiguous. */</span></span>
<span class="line"><span> color</span><span>: </span><span>var</span><span>(</span><span>--system-color-ferrari</span><span>);</span></span>
<span class="line"><span>}</span></span>
<span class="line"></span>
<span class="line"><span>button.destructive</span><span> {</span></span>
<span class="line"><span> /* Clearer now at a glance that a red color was correctly set */</span></span>
<span class="line"><span> color</span><span>: </span><span>var</span><span>(</span><span>--system-color-ferrariRed</span><span>);</span></span>
<span class="line"><span>}</span></span></code></pre>
<p>This naming convention can be expanded to incorporate alpha, though it’s a bit of a stretch. Separately from naming, just as general practice, it’s often better to reach non-opaque color values in UI via layered transformations or usage-based named variables than to put them into the static color palette. That said, when I <em>have</em> needed to write a value-based variable name for a non-opaque value using this convention I’ve put that info at the start of the color name using terms analogous to real world transparency.</p>
<pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>:root</span><span> {</span></span>
<span class="line"><span> --system-color-semitransparentBondiBlue</span><span>: </span><span>rgb</span><span>(</span><span>0</span><span> 58</span><span> 71</span><span> / </span><span>10%</span><span>);</span></span>
<span class="line"><span> --system-color-translucentBondiBlue</span><span>: </span><span>rgb</span><span>(</span><span>0</span><span> 58</span><span> 71</span><span> / </span><span>30%</span><span>);</span></span>
<span class="line"><span> --system-color-frostedBondiBlue</span><span>: </span><span>rgb</span><span>(</span><span>0</span><span> 58</span><span> 71</span><span> / </span><span>70%</span><span>);</span></span>
<span class="line"><span>}</span></span></code></pre>
<h3 id="where-to-use-usage-based-naming">Where to use usage-based naming</h3>
<p>Variable names tied to use provide varying levels of abstraction. Put another way, names can communicate different scopes of capability and utility within the project by describing uses that are more specific or more general. Some are very narrow in use because they describe a very specific thing and some are very wide in use because they describe a general category of things.</p>
<p>For a very contrived example, consider naming the font weight used in a button that submits a registration form. That variable could be named something like <code>--fontWeight-regFormSubmitButton</code> but that’s very specific. Typically all submit buttons look the same way in which case the concept of a ‘submit button font weight’ could be abstracted out into a less specific name like <code>--fontWeight-submitButton</code>. That name is more general and as a result at a higher level abstraction because it doesn’t refer to a specific form anymore.</p>
<p>It often makes sense to combine variables with multiple levels of abstraction in a project. Here is how that could come into play with control tinting:</p>
<pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>:root</span><span> {</span></span>
<span class="line"><span> /* Color palette defined at root */</span></span>
<span class="line"><span> --system-color-bondiBlue</span><span>: </span><span>rgb</span><span>(</span><span>0</span><span> 58</span><span> 71</span><span>);</span></span>
<span class="line"><span> --system-color-canaryYellow</span><span>: </span><span>rgb</span><span>(</span><span>255</span><span> 239</span><span> 0</span><span>);</span></span>
<span class="line"><span>}</span></span>
<span class="line"></span>
<span class="line"><span>body</span><span> {</span></span>
<span class="line"><span> /* Custom property for custom controls */</span></span>
<span class="line"><span> --color-accentColor</span><span>: </span><span>var</span><span>(</span><span>--system-color-bondiBlue</span><span>);</span></span>
<span class="line"><span> /* Reflect it below for native controls */</span></span>
<span class="line"><span> accent-color</span><span>: </span><span>var</span><span>(</span><span>--color-accentColor</span><span>);</span></span>
<span class="line"><span>}</span></span>
<span class="line"></span>
<span class="line"><span>foobar-custom-control</span><span> {</span></span>
<span class="line"><span> /* Define CSS interface allowing the background to be changed */</span></span>
<span class="line"><span> --color-background</span><span>: </span><span>var</span><span>(</span><span>--accentColor</span><span>);</span></span>
<span class="line"><span> /* Implement the above interface */</span></span>
<span class="line"><span> background</span><span>: </span><span>var</span><span>(</span><span>--color-background</span><span>);</span></span>
<span class="line"><span>}</span></span>
<span class="line"></span>
<span class="line"><span>form.tinted</span><span> {</span></span>
<span class="line"><span> /* Define CSS interface for applying tint colors to form */</span></span>
<span class="line"><span> --color-formTint</span><span>: </span><span>var</span><span>(</span><span>--system-color-canaryYellow</span><span>);</span></span>
<span class="line"><span>}</span></span>
<span class="line"></span>
<span class="line"><span>form.tinted</span><span> foobar-custom-control</span><span> {</span></span>
<span class="line"><span> /* Utilize the above interface for foobar-custom-control */</span></span>
<span class="line"><span> --color-background</span><span>: </span><span>var</span><span>(</span><span>--color-formTint</span><span>);</span></span>
<span class="line"><span>}</span></span></code></pre>
<h3 id="dark-mode-is-simpler-with-usage-based-variables">Dark mode is simpler with usage-based variables</h3>
<p>Consider an implementation of dark mode styling without usage-based variables vs one with them. When using only value-based variables, the code is much more repetitive and verbose.</p>
<pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>/* Dark mode WITHOUT usage-based variables… */</span></span>
<span class="line"></span>
<span class="line"><span>:root</span><span> {</span></span>
<span class="line"><span> --system-color-deepBlack</span><span>: </span><span>#333</span><span>;</span></span>
<span class="line"><span> --system-color-offWhite</span><span>: </span><span>#eee</span><span>;</span></span>
<span class="line"><span> --system-color-skyBlue</span><span>: </span><span>lch</span><span>(</span><span>33</span><span> 111</span><span> 231.17</span><span>);</span></span>
<span class="line"><span> --system-color-deepBlue</span><span>: </span><span>lch</span><span>(</span><span>14</span><span> 111</span><span> 231.17</span><span>);</span></span>
<span class="line"><span>}</span></span>
<span class="line"></span>
<span class="line"><span>/* Because the variables above are named in</span></span>
<span class="line"><span> a value-based way we can’t reasonably change</span></span>
<span class="line"><span> their values. Instead we fork our CSS below</span></span>
<span class="line"><span> to use one or the other depending on the</span></span>
<span class="line"><span> root appearance. */</span></span>
<span class="line"></span>
<span class="line"><span>[</span><span>data-appearance</span><span>=</span><span>"light"</span><span>] </span><span>body</span><span> {</span></span>
<span class="line"><span> color</span><span>: </span><span>var</span><span>(</span><span>--system-color-deepBlack</span><span>);</span></span>
<span class="line"><span> background</span><span>: </span><span>var</span><span>(</span><span>--system-color-offWhite</span><span>);</span></span>
<span class="line"><span>}</span></span>
<span class="line"></span>
<span class="line"><span>[</span><span>data-appearance</span><span>=</span><span>"dark"</span><span>] </span><span>body</span><span> {</span></span>
<span class="line"><span> color</span><span>: </span><span>var</span><span>(</span><span>--system-color-offWhite</span><span>);</span></span>
<span class="line"><span> background</span><span>: </span><span>var</span><span>(</span><span>--system-color-deepBlack</span><span>);</span></span>
<span class="line"><span>}</span></span>
<span class="line"></span>
<span class="line"><span>[</span><span>data-appearance</span><span>=</span><span>"light"</span><span>] </span><span>a</span><span> {</span></span>
<span class="line"><span> color</span><span>: </span><span>var</span><span>(</span><span>--system-color-deepBlue</span><span>);</span></span>
<span class="line"><span>}</span></span>
<span class="line"></span>
<span class="line"><span>[</span><span>data-appearance</span><span>=</span><span>"dark"</span><span>] </span><span>a</span><span> {</span></span>
<span class="line"><span> color</span><span>: </span><span>var</span><span>(</span><span>--system-color-skyBlue</span><span>);</span></span>
<span class="line"><span>}</span></span></code></pre>
<p>With usage-based names you can define color variables as interface concepts that have understood meanings beyond individual UI pieces allowing for those values to be externally changed for dark mode without needing to maintain separate light/dark CSS for each new piece of UI.</p>
<pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>/* Dark mode WITH usage-based variables… */</span></span>
<span class="line"></span>
<span class="line"><span>[</span><span>data-appearance</span><span>=</span><span>"light"</span><span>] {</span></span>
<span class="line"><span> --system-color-textPrimary</span><span>: </span><span>#333</span><span>;</span></span>
<span class="line"><span> --system-color-fillPrimary</span><span>: </span><span>#eee</span><span>;</span></span>
<span class="line"><span> --system-color-link</span><span>: </span><span>lch</span><span>(</span><span>14</span><span> 111</span><span> 231.17</span><span>);</span></span>
<span class="line"><span>}</span></span>
<span class="line"><span>[</span><span>data-appearance</span><span>=</span><span>"dark"</span><span>] {</span></span>
<span class="line"><span> --system-color-textPrimary</span><span>: </span><span>#eee</span><span>;</span></span>
<span class="line"><span> --system-color-fillPrimary</span><span>: </span><span>#333</span><span>;</span></span>
<span class="line"><span> --system-color-link</span><span>: </span><span>lch</span><span>(</span><span>33</span><span> 111</span><span> 231.17</span><span>);</span></span>
<span class="line"><span>}</span></span>
<span class="line"></span>
<span class="line"><span>/* With usage-specific variable names we can</span></span>
<span class="line"><span> change the values for the uses they describe</span></span>
<span class="line"><span> at a very high level of abstraction allowing</span></span>
<span class="line"><span> the lower level code that uses the variables</span></span>
<span class="line"><span> not to need to understand the current</span></span>
<span class="line"><span> appearance mode. */</span></span>
<span class="line"></span>
<span class="line"><span>body</span><span> {</span></span>
<span class="line"><span> color</span><span>: </span><span>var</span><span>(</span><span>--system-color-textPrimary</span><span>);</span></span>
<span class="line"><span> background</span><span>: </span><span>var</span><span>(</span><span>--system-color-fillPrimary</span><span>);</span></span>
<span class="line"><span>}</span></span>
<span class="line"></span>
<span class="line"><span>a</span><span> {</span></span>
<span class="line"><span> color</span><span>: </span><span>var</span><span>(</span><span>--system-color-link</span><span>);</span></span>
<span class="line"><span>}</span></span></code></pre>
<h3 id="levels-of-hierarchy-within-usage-based-variables">Levels of hierarchy within usage-based variables</h3>
<p>Any time you have a design that references the same value across mulitple pieces of UI, I’d suggest that is an opportunity for abstracting that value into a name that better describes the intention of the value in the design.</p>
<p>For example, if the background color of your sidebar and your info panels are both <code>#eee</code>, relative to the <code>#fff</code> of your main background, perhaps your intent for that color from the design perspective is to convey to the user that UI with that background is of a secondary nature.</p>
<pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>:root</span><span> {</span></span>
<span class="line"><span> --system-color-backgroundPrimary</span><span>: </span><span>#fff</span><span>;</span></span>
<span class="line"><span> --system-color-backgroundSecondary</span><span>: </span><span>#eee</span><span>;</span></span>
<span class="line"><span>}</span></span></code></pre>
<p>The utility of a usage-based name comes in how it guides a developer or designer in its use. Be careful to avoid using names the are too generic. For example, <code>--system-color-primary</code> is too open-ended in meaning making it unclear where it should be used.</p>
<pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>/* Do not do this */</span></span>
<span class="line"></span>
<span class="line"><span>:root</span><span> {</span></span>
<span class="line"><span> /* What is this for? */</span></span>
<span class="line"><span> --system-color-box</span><span>: </span><span>var</span><span>(</span><span>--system-color-neonBlue</span><span>);</span></span>
<span class="line"><span>}</span></span>
<span class="line"></span>
<span class="line"><span>:is</span><span>(</span><span>a</span><span>, </span><span>button</span><span>, </span><span>input</span><span>)</span><span>:focus-visible</span><span> {</span></span>
<span class="line"><span> /* Was this variable used correctly? */</span></span>
<span class="line"><span> background</span><span>: </span><span>var</span><span>(</span><span>--system-color-box</span><span>);</span></span>
<span class="line"><span>}</span></span></code></pre>
<pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>/* Do this instead */</span></span>
<span class="line"></span>
<span class="line"><span>:root</span><span> {</span></span>
<span class="line"><span> /* Clear what this is for */</span></span>
<span class="line"><span> --system-color-focusRing</span><span>: </span><span>var</span><span>(</span><span>--system-color-neonBlue</span><span>);</span></span>
<span class="line"><span>}</span></span>
<span class="line"></span>
<span class="line"><span>:is</span><span>(</span><span>a</span><span>, </span><span>button</span><span>, </span><span>input</span><span>)</span><span>:focus-visible</span><span> {</span></span>
<span class="line"><span> /* Clear it was used correctly */</span></span>
<span class="line"><span> outline-color</span><span>: </span><span>var</span><span>(</span><span>--system-color-focusRing</span><span>);</span></span>
<span class="line"><span>}</span></span></code></pre>
<p>Be careful to avoid using names that are too specific. For example, <code>--system-color-mainToolbarBackground</code> could only be used in one spot which makes the use of a variable superfluous.</p>
<pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>/* Do not do this */</span></span>
<span class="line"></span>
<span class="line"><span>:root</span><span> {</span></span>
<span class="line"><span> /* This can only be used in one place meaning its</span></span>
<span class="line"><span> existence needlessly adds complexity to the project */</span></span>
<span class="line"><span> --system-color-mainToolbarBackground</span><span>: </span><span>#eee</span><span>;</span></span>
<span class="line"><span>}</span></span>
<span class="line"></span>
<span class="line"><span>main</span><span> .toolbar</span><span> {</span></span>
<span class="line"><span> background</span><span>: </span><span>var</span><span>(</span><span>--system-color-mainToolbarBackground</span><span>);</span></span>
<span class="line"><span>}</span></span></code></pre>
<pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>/* Do this instead */</span></span>
<span class="line"></span>
<span class="line"><span>:root</span><span> {</span></span>
<span class="line"><span> /* This name is general enough in scope that it</span></span>
<span class="line"><span> can be used across all UI so it is useful at</span></span>
<span class="line"><span> the global level. */</span></span>
<span class="line"><span> --system-color-backgroundSecondary</span><span>: </span><span>#eee</span><span>;</span></span>
<span class="line"><span>}</span></span>
<span class="line"></span>
<span class="line"><span>main</span><span> .toolbar</span><span> {</span></span>
<span class="line"><span> background</span><span>: </span><span>var</span><span>(</span><span>--system-color-backgroundSecondary</span><span>);</span></span>
<span class="line"><span>}</span></span></code></pre>
<h2 id="wrapping-up">Wrapping up</h2>
<p>There’s a lot more to say on this topic but I’m going to end here for now. Please feel free to reach out, there are links in the footer. I’d love to continue the discussion!</p>

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

@@ -118,8 +118,12 @@
<li><a href="/david/cache/2024/6c3a6c638625148d7bb6a5c438c9b22a/" title="Accès à l’article dans le cache local : Yearnotes #4 • détour.studio">Yearnotes #4 • détour.studio</a> (<a href="https://détour.studio/yearnotes/4/" title="Accès à l’article original distant : Yearnotes #4 • détour.studio">original</a>)</li>
<li><a href="/david/cache/2024/d0ffe1891c332b6fc6e7d7826d8489da/" title="Accès à l’article dans le cache local : Naming Variables In CSS">Naming Variables In CSS</a> (<a href="https://jwdallas.com/posts/namingcssvariables/" title="Accès à l’article original distant : Naming Variables In CSS">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>
<li><a href="/david/cache/2024/44c12c8fbb59c7239f0f3b04bae189b7/" title="Accès à l’article dans le cache local : Tailwind marketing and misinformation engine">Tailwind marketing and misinformation engine</a> (<a href="https://nuejs.org/blog/tailwind-misinformation-engine/" title="Accès à l’article original distant : Tailwind marketing and misinformation engine">original</a>)</li>
<li><a href="/david/cache/2024/7a223e552e8a8e3e11c759cbc5bc3ffa/" title="Accès à l’article dans le cache local : Opening Mail / frantic.im">Opening Mail / frantic.im</a> (<a href="https://frantic.im/opening-mail/" title="Accès à l’article original distant : Opening Mail / frantic.im">original</a>)</li>
<li><a href="/david/cache/2024/9750840b448f07a2f2594bb25da23836/" title="Accès à l’article dans le cache local : Tech doesn’t make our lives easier. It makes them faster">Tech doesn’t make our lives easier. It makes them faster</a> (<a href="https://brettscott.substack.com/p/tech-doesnt-make-our-lives-easier" title="Accès à l’article original distant : Tech doesn’t make our lives easier. It makes them faster">original</a>)</li>
@@ -168,6 +172,8 @@
<li><a href="/david/cache/2024/291cddda62f18ec9355ec98761b7e9d9/" title="Accès à l’article dans le cache local : Writing with AI">Writing with AI</a> (<a href="https://ia.net/topics/writing-with-ai" title="Accès à l’article original distant : Writing with AI">original</a>)</li>
<li><a href="/david/cache/2024/20d288eb47779c4f1b3f36fb86aa7108/" title="Accès à l’article dans le cache local : File over app - Steph Ango">File over app - Steph Ango</a> (<a href="https://stephango.com/file-over-app" title="Accès à l’article original distant : File over app - Steph Ango">original</a>)</li>
<li><a href="/david/cache/2024/82b88d48d8043d79425ce8afd8dff42e/" title="Accès à l’article dans le cache local : Echoing Wirth's plea for lean software">Echoing Wirth's plea for lean software</a> (<a href="https://blog.testdouble.com/posts/2024-01-24-plea-for-lean/" title="Accès à l’article original distant : Echoing Wirth's plea for lean software">original</a>)</li>
<li><a href="/david/cache/2024/40a1a0f90d7ac25a6150e6e27ac310d4/" title="Accès à l’article dans le cache local : D’un enfant à l’autre">D’un enfant à l’autre</a> (<a href="https://www.la-grange.net/2024/02/04/enfant" title="Accès à l’article original distant : D’un enfant à l’autre">original</a>)</li>
@@ -196,6 +202,8 @@
<li><a href="/david/cache/2024/55477786fc56b6fc37bb97231b634d90/" title="Accès à l’article dans le cache local : Fabrique : concept">Fabrique : concept</a> (<a href="https://www.quaternum.net/2023/06/02/fabrique-concept/" title="Accès à l’article original distant : Fabrique : concept">original</a>)</li>
<li><a href="/david/cache/2024/6b26bff7f4772cf8fb78878ff4f9594f/" title="Accès à l’article dans le cache local : command center: Simplicity">command center: Simplicity</a> (<a href="https://commandcenter.blogspot.com/2023/12/simplicity.html" title="Accès à l’article original distant : command center: Simplicity">original</a>)</li>
<li><a href="/david/cache/2024/75c7b6350ba18a5a11ee3bbf8b3b64be/" title="Accès à l’article dans le cache local : Google will no longer back up the Internet: Cached webpages are dead">Google will no longer back up the Internet: Cached webpages are dead</a> (<a href="https://arstechnica.com/gadgets/2024/02/google-search-kills-off-cached-webpages/" title="Accès à l’article original distant : Google will no longer back up the Internet: Cached webpages are dead">original</a>)</li>
<li><a href="/david/cache/2024/1929f7183f694c7abeafeddb891fcf50/" title="Accès à l’article dans le cache local : Crise des opioïdes : pourquoi il ne faut ni l'oublier ni l'ignorer">Crise des opioïdes : pourquoi il ne faut ni l'oublier ni l'ignorer</a> (<a href="https://basta.media/crise-des-opioides-pourquoi-il-ne-faut-ni-oublier-ni-ignorer" title="Accès à l’article original distant : Crise des opioïdes : pourquoi il ne faut ni l'oublier ni l'ignorer">original</a>)</li>

Loading…
Cancel
Save