A place to cache linked articles (think custom and personal wayback machine)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.html 31KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. <!doctype html><!-- This is a valid HTML5 document. -->
  2. <!-- Screen readers, SEO, extensions and so on. -->
  3. <html lang="">
  4. <!-- Has to be within the first 1024 bytes, hence before the `title` element
  5. See: https://www.w3.org/TR/2012/CR-html5-20121217/document-metadata.html#charset -->
  6. <meta charset="utf-8">
  7. <!-- Why no `X-UA-Compatible` meta: https://stackoverflow.com/a/6771584 -->
  8. <!-- The viewport meta is quite crowded and we are responsible for that.
  9. See: https://codepen.io/tigt/post/meta-viewport-for-2015 -->
  10. <meta name="viewport" content="width=device-width,initial-scale=1">
  11. <!-- Required to make a valid HTML5 document. -->
  12. <title>Tailwind marketing and misinformation engine (archive) — David Larlet</title>
  13. <meta name="description" content="Publication mise en cache pour en conserver une trace.">
  14. <!-- That good ol' feed, subscribe :). -->
  15. <link rel="alternate" type="application/atom+xml" title="Feed" href="/david/log/">
  16. <!-- Generated from https://realfavicongenerator.net/ such a mess. -->
  17. <link rel="apple-touch-icon" sizes="180x180" href="/static/david/icons2/apple-touch-icon.png">
  18. <link rel="icon" type="image/png" sizes="32x32" href="/static/david/icons2/favicon-32x32.png">
  19. <link rel="icon" type="image/png" sizes="16x16" href="/static/david/icons2/favicon-16x16.png">
  20. <link rel="manifest" href="/static/david/icons2/site.webmanifest">
  21. <link rel="mask-icon" href="/static/david/icons2/safari-pinned-tab.svg" color="#07486c">
  22. <link rel="shortcut icon" href="/static/david/icons2/favicon.ico">
  23. <meta name="msapplication-TileColor" content="#f7f7f7">
  24. <meta name="msapplication-config" content="/static/david/icons2/browserconfig.xml">
  25. <meta name="theme-color" content="#f7f7f7" media="(prefers-color-scheme: light)">
  26. <meta name="theme-color" content="#272727" media="(prefers-color-scheme: dark)">
  27. <!-- Is that even respected? Retrospectively? What a shAItshow…
  28. https://neil-clarke.com/block-the-bots-that-feed-ai-models-by-scraping-your-website/ -->
  29. <meta name="robots" content="noai, noimageai">
  30. <!-- Documented, feel free to shoot an email. -->
  31. <link rel="stylesheet" href="/static/david/css/style_2021-01-20.css">
  32. <!-- See https://www.zachleat.com/web/comprehensive-webfonts/ for the trade-off. -->
  33. <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>
  34. <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>
  35. <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>
  36. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  37. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  38. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  39. <script>
  40. function toggleTheme(themeName) {
  41. document.documentElement.classList.toggle(
  42. 'forced-dark',
  43. themeName === 'dark'
  44. )
  45. document.documentElement.classList.toggle(
  46. 'forced-light',
  47. themeName === 'light'
  48. )
  49. }
  50. const selectedTheme = localStorage.getItem('theme')
  51. if (selectedTheme !== 'undefined') {
  52. toggleTheme(selectedTheme)
  53. }
  54. </script>
  55. <meta name="robots" content="noindex, nofollow">
  56. <meta content="origin-when-cross-origin" name="referrer">
  57. <!-- Canonical URL for SEO purposes -->
  58. <link rel="canonical" href="https://nuejs.org/blog/tailwind-misinformation-engine/">
  59. <body class="remarkdown h1-underline h2-underline h3-underline em-underscore hr-center ul-star pre-tick" data-instant-intensity="viewport-all">
  60. <article>
  61. <header>
  62. <h1>Tailwind marketing and misinformation engine</h1>
  63. </header>
  64. <nav>
  65. <p class="center">
  66. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  67. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
  68. </svg> Accueil</a> •
  69. <a href="https://nuejs.org/blog/tailwind-misinformation-engine/" title="Lien vers le contenu original">Source originale</a>
  70. <br>
  71. Mis en cache le 2024-02-18
  72. </p>
  73. </nav>
  74. <hr>
  75. <figure class="author">
  76. <a href="https://nuejs.org/blog/backstory/"><img src="https://nuejs.org/global/img/tero.png"></a>
  77. <figcaption>
  78. <h4>Tero Piirainen</h4>
  79. <p><a href="//twitter.com/tipiirai">@tipiirai</a></p>
  80. </figcaption>
  81. </figure>
  82. <p>Tailwind CSS was born out of this sentence:</p>
  83. <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>
  84. <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>
  85. <h2 id="origins">Phase 1: The Origins of Tailwind<a href="#origins" title='Permlink for "Phase 1: The Origins of Tailwind"'></a></h2>
  86. <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>
  87. <pre glow><code language="html"><i>&lt;</i><strong>nav </strong><b>class</b><i>=</i><em>"<mark>uilist</mark>"</em><i>&gt;</i>
  88. <i>&lt;</i><strong>span </strong><b>class</b><i>=</i><em>"<mark>uilist-item</mark>"</em><i>&gt;</i>
  89. <i>.</i><i>.</i><i>.</i>
  90. <i>&lt;</i><i>/</i><strong>span</strong><i>&gt;</i>
  91. <i>&lt;</i><i>/</i><strong>nav</strong><i>&gt;</i></code></pre>
  92. <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>
  93. <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>
  94. <pre glow><code language="html"><sup>&lt;!-- "uilist" --&gt;</sup>
  95. <i>&lt;</i><strong>div </strong><b>class</b><i>=</i><i>"</i>
  96. <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>
  97. <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>
  98. <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>
  99. <dfn> <b>dark</b><i>:</i>bg<i>-</i>transparent<i>"</i><i>&gt;</i></dfn>
  100. <sup> &lt;!-- "uilist-item" --&gt;</sup>
  101. <i>&lt;</i><strong>span </strong><b>class</b><i>=</i><i>"</i>
  102. <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>
  103. <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>
  104. <i>.</i><i>.</i><i>.</i>
  105. <i>&lt;</i><i>/</i><strong>span</strong><i>&gt;</i>
  106. <i>&lt;</i><i>/</i><strong>div</strong><i>&gt;</i></code></pre>
  107. <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>
  108. <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>
  109. <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>
  110. <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>
  111. <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>
  112. <p>The new approach can be summarized as follows:</p>
  113. <blockquote>
  114. <p><a href="//tailwindcss.com/">"Semantic class names” are the reason CSS is hard to maintain</a></p>
  115. </blockquote>
  116. <p>This was a hefty statement as it contradicts with all the prior work and studies about CSS.</p>
  117. <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>
  118. <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>
  119. <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>
  120. <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>
  121. <h2 id="phase2">Phase 2: Utility-first workflow<a href="#phase2" title='Permlink for "Phase 2: Utility-first workflow"'></a></h2>
  122. <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>
  123. <blockquote>
  124. <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>
  125. </blockquote>
  126. <p>Here's how the flow works:</p>
  127. <h3 id="step-1-onboarding">Step 1: Onboarding<a href="#step-1-onboarding" title='Permlink for "Step 1: Onboarding"'></a></h3>
  128. <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>
  129. <blockquote>
  130. <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>
  131. </blockquote>
  132. <p>Sounds good, so let's try it.</p>
  133. <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>
  134. <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>
  135. <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>
  136. <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>
  137. <blockquote>
  138. <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>
  139. </blockquote>
  140. <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>
  141. <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>
  142. <p>So I keep coming back to the first step resulting in more and more utility classes. I'm locked inside a loop:</p>
  143. <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>
  144. <p>I find this a rather clever way to lock people using Tailwind, resulting in more retention, loyalty, and money.</p>
  145. <h2 id="catalyst">Phase 3: Catalyst UI kit<a href="#catalyst" title='Permlink for "Phase 3: Catalyst UI kit"'></a></h2>
  146. <p>In December 2023, Tailwind introduced <em>Catalyst</em> with a richer set of language expressions and a React-based UI library.</p>
  147. <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>
  148. <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>
  149. <p>Let's look at the source code of the first button on <a href="//catalyst.tailwindui.com/">Catalyst demo page</a>:</p>
  150. <p><img src="https://nuejs.org/img/tailwind-button.png" loading="lazy"></p>
  151. <section class="small section">
  152. <p>The black button source code. The expressions are sorted alphabetically:</p>
  153. </section>
  154. <pre glow class="small"><code language="html"><i>&lt;</i><strong>button </strong><b>class</b><i>=</i><i>"</i>
  155. <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>
  156. <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>
  157. <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>
  158. <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>
  159. <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>
  160. <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>
  161. <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>
  162. <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>
  163. <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>
  164. <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>
  165. <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>
  166. <b>after</b><i>:</i><i>-</i>z<i>-</i><em>10</em>
  167. <b>after</b><i>:</i>absolute
  168. <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>
  169. <b>after</b><i>:</i><b>data-</b><i>[</i>disabled<i>]</i><i>:</i>shadow<i>-</i>none
  170. <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>
  171. <b>after</b><i>:</i>inset<i>-</i><em>0</em>
  172. <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>
  173. <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>
  174. <b>before</b><i>:</i><i>-</i>z<i>-</i><em>10</em>
  175. <b>before</b><i>:</i>absolute
  176. <b>before</b><i>:</i><b>bg-</b><i>[</i><i>-</i><i>-</i>btn<i>-</i>bg<i>]</i>
  177. <b>before</b><i>:</i><b>data-</b><i>[</i>disabled<i>]</i><i>:</i>shadow<i>-</i>none
  178. <b>before</b><i>:</i>inset<i>-</i><em>0</em>
  179. <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>
  180. <b>before</b><i>:</i>shadow
  181. <b>bg-</b><i>[</i><i>-</i><i>-</i>btn<i>-</i>border<i>]</i>
  182. border
  183. border<i>-</i>transparent
  184. <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>
  185. <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>
  186. <b>dark</b><i>:</i><b>after</b><i>:</i><i>-</i>inset<i>-</i>px
  187. <b>dark</b><i>:</i><b>after</b><i>:</i>rounded<i>-</i>lg
  188. <b>dark</b><i>:</i><b>before</b><i>:</i>hidden
  189. <b>dark</b><i>:</i><b>bg-</b><i>[</i><i>-</i><i>-</i>btn<i>-</i>bg<i>]</i>
  190. <b>dark</b><i>:</i>border<i>-</i>white<i>/</i><em>5</em>
  191. <b>dark</b><i>:</i>text<i>-</i>white
  192. <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>
  193. <b>data-</b><i>[</i>disabled<i>]</i><i>:</i>opacity<i>-</i><em>50</em>
  194. <b>data-</b><i>[</i>focus<i>]</i><i>:</i>outline
  195. <b>data-</b><i>[</i>focus<i>]</i><i>:</i>outline<i>-</i><em>2</em>
  196. <b>data-</b><i>[</i>focus<i>]</i><i>:</i>outline<i>-</i>blue<i>-</i><em>500</em>
  197. <b>data-</b><i>[</i>focus<i>]</i><i>:</i>outline<i>-</i>offset<i>-</i><em>2</em>
  198. <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>
  199. <b>focus</b><i>:</i>outline<i>-</i>none
  200. font<i>-</i>semibold
  201. <b>forced-colors</b><i>:</i><i>[</i><i>-</i><i>-</i><b>btn-icon</b><i>:</i>ButtonText<i>]</i>
  202. <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>
  203. gap<i>-</i>x<i>-</i><em>2</em>
  204. inline<i>-</i>flex
  205. isolate
  206. items<i>-</i>center
  207. justify<i>-</i>center
  208. <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>
  209. <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>
  210. relative
  211. rounded<i>-</i>lg
  212. <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>
  213. <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>
  214. <b>sm</b><i>:</i>text<i>-</i>sm<i>/</i><em>6</em>
  215. text<i>-</i>base<i>/</i><em>6</em>
  216. text<i>-</i>white<i>"</i><i>&gt;</i> Button <i>&lt;</i><i>/</i><strong>button</strong><i>&gt;</i></code></pre>
  217. <p>I have many questions about this:</p>
  218. <p>Most importantly: how is this wall of text more maintainable than a class name like "primary"?</p>
  219. <p>Do I need another wall for the white button?</p>
  220. <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>
  221. <h3 id="modeled-after-html">"Modeled after HTML"<a href="#modeled-after-html" title='Permlink for ""Modeled after HTML""'></a></h3>
  222. <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>
  223. <pre glow><code language="html"><span><i>&lt;</i><strong>Dialog</strong><i>&gt;</i></span>
  224. <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>
  225. <span> <i>&lt;</i><strong>DialogDescription</strong><i>&gt;</i></span>
  226. <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>
  227. <span> <i>&lt;</i><i>/</i><strong>DialogDescription</strong><i>&gt;</i></span>
  228. <span></span>
  229. <span> <i>&lt;</i><strong>DialogBody</strong><i>&gt;</i></span>
  230. <span> <i>&lt;</i><strong>Field</strong><i>&gt;</i></span>
  231. <span> <i>&lt;</i><strong>Label</strong><i>&gt;</i>Email<i>&lt;</i><i>/</i><strong>Label</strong><i>&gt;</i></span>
  232. <span> <i>&lt;</i><strong>Input </strong><b>name</b><i>=</i><em>"email"</em> <i>/</i><i>&gt;</i></span>
  233. <span> <i>&lt;</i><i>/</i><strong>Field</strong><i>&gt;</i></span>
  234. <span> <i>&lt;</i><i>/</i><strong>DialogBody</strong><i>&gt;</i></span>
  235. <span></span>
  236. <span> <i>&lt;</i><strong>DialogActions</strong><i>&gt;</i></span>
  237. <span> <i>&lt;</i><strong>Button </strong>plain<i>&gt;</i>Cancel<i>&lt;</i><i>/</i><strong>Button</strong><i>&gt;</i></span>
  238. <span> <i>&lt;</i><strong>Button</strong><i>&gt;</i>Join<i>&lt;</i><i>/</i><strong>Button</strong><i>&gt;</i></span>
  239. <span> <i>&lt;</i><i>/</i><strong>DialogActions</strong><i>&gt;</i></span>
  240. <span><i>&lt;</i><i>/</i><strong>Dialog</strong><i>&gt;</i></span></code></pre>
  241. <p>The markup feels surprisingly similar to semantic HTML:</p>
  242. <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>
  243. <p>This raises more questions:</p>
  244. <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>
  245. <p>And standard HTML <code>&lt;dialog&gt;</code> is bad, but <code>&lt;Dialog&gt;</code> with uppercase is legit?</p>
  246. <p>Why introduce so many different versions of the <code>&lt;p&gt;</code> tag?</p>
  247. <pre glow><code language="html"><sup>&lt;!-- Catalyst &lt;p&gt; tags --&gt;</sup>
  248. <i>&lt;</i><strong>Text</strong><i>&gt;</i>
  249. <i>&lt;</i><strong>Description</strong><i>&gt;</i>
  250. <i>&lt;</i><strong>DialogDescription</strong><i>&gt;</i>
  251. <i>&lt;</i><strong>AlertDescription</strong><i>&gt;</i>
  252. <i>.</i><i>.</i><i>.</i></code></pre>
  253. <p>Why is content-aware naming okay in element names but bad in class names?</p>
  254. <p>Is separation of concerns suddenly okay with Catalyst, but bad with vanilla HTML and CSS?</p>
  255. <p>I'm confused, to say the least.</p>
  256. <hr>
  257. <h2 id="i-love-css">I love ❤️ CSS<a href="#i-love-css" title='Permlink for "I love ❤️ CSS"'></a></h2>
  258. <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>
  259. <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>
  260. <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>
  261. <p>We're using maybe 30% of the full potential.</p>
  262. <p>I'm not a fan of any of that.</p>
  263. <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>
  264. <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>
  265. <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>
  266. <ol>
  267. <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>
  268. </li>
  269. <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>
  270. </li>
  271. <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>
  272. </li>
  273. </ol>
  274. <h3 id="content-first">Content first<a href="#content-first" title='Permlink for "Content first"'></a></h3>
  275. <p>Here's a better workflow. It has many names: "standards first", "content first", or "progressive enhancement".</p>
  276. <figure class="tall"><img src="https://nuejs.org/img/standards-first.png" alt="Standards first model" loading="lazy"><figcaption>Standards first model</figcaption></figure>
  277. <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>
  278. <blockquote>
  279. <p>Clean code is easier to maintain</p>
  280. </blockquote>
  281. <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>
  282. <p>Because trends are temporary, but standards are forever.</p>
  283. <h3 id="stay-relevant">Stay relevant<a href="#stay-relevant" title='Permlink for "Stay relevant"'></a></h3>
  284. <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>
  285. <p>Learn to write clean HTML and CSS and stay relevant for years to come.</p>
  286. <p><img src="https://nuejs.org/img/tw-switch.png" loading="lazy" class="tall"></p>
  287. </article>
  288. <hr>
  289. <footer>
  290. <p>
  291. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  292. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
  293. </svg> Accueil</a> •
  294. <a href="/david/log/" title="Accès au flux RSS"><svg class="icon icon-rss2">
  295. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-rss2"></use>
  296. </svg> Suivre</a> •
  297. <a href="http://larlet.com" title="Go to my English profile" data-instant><svg class="icon icon-user-tie">
  298. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-user-tie"></use>
  299. </svg> Pro</a> •
  300. <a href="mailto:david%40larlet.fr" title="Envoyer un courriel"><svg class="icon icon-mail">
  301. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-mail"></use>
  302. </svg> Email</a> •
  303. <abbr class="nowrap" title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340"><svg class="icon icon-hammer2">
  304. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-hammer2"></use>
  305. </svg> Légal</abbr>
  306. </p>
  307. <template id="theme-selector">
  308. <form>
  309. <fieldset>
  310. <legend><svg class="icon icon-brightness-contrast">
  311. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-brightness-contrast"></use>
  312. </svg> Thème</legend>
  313. <label>
  314. <input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
  315. </label>
  316. <label>
  317. <input type="radio" value="dark" name="chosen-color-scheme"> Foncé
  318. </label>
  319. <label>
  320. <input type="radio" value="light" name="chosen-color-scheme"> Clair
  321. </label>
  322. </fieldset>
  323. </form>
  324. </template>
  325. </footer>
  326. <script src="/static/david/js/instantpage-5.1.0.min.js" type="module"></script>
  327. <script>
  328. function loadThemeForm(templateName) {
  329. const themeSelectorTemplate = document.querySelector(templateName)
  330. const form = themeSelectorTemplate.content.firstElementChild
  331. themeSelectorTemplate.replaceWith(form)
  332. form.addEventListener('change', (e) => {
  333. const chosenColorScheme = e.target.value
  334. localStorage.setItem('theme', chosenColorScheme)
  335. toggleTheme(chosenColorScheme)
  336. })
  337. const selectedTheme = localStorage.getItem('theme')
  338. if (selectedTheme && selectedTheme !== 'undefined') {
  339. form.querySelector(`[value="${selectedTheme}"]`).checked = true
  340. }
  341. }
  342. const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
  343. window.addEventListener('load', () => {
  344. let hasDarkRules = false
  345. for (const styleSheet of Array.from(document.styleSheets)) {
  346. let mediaRules = []
  347. for (const cssRule of styleSheet.cssRules) {
  348. if (cssRule.type !== CSSRule.MEDIA_RULE) {
  349. continue
  350. }
  351. // WARNING: Safari does not have/supports `conditionText`.
  352. if (cssRule.conditionText) {
  353. if (cssRule.conditionText !== prefersColorSchemeDark) {
  354. continue
  355. }
  356. } else {
  357. if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
  358. continue
  359. }
  360. }
  361. mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
  362. }
  363. // WARNING: do not try to insert a Rule to a styleSheet you are
  364. // currently iterating on, otherwise the browser will be stuck
  365. // in a infinite loop…
  366. for (const mediaRule of mediaRules) {
  367. styleSheet.insertRule(mediaRule.cssText)
  368. hasDarkRules = true
  369. }
  370. }
  371. if (hasDarkRules) {
  372. loadThemeForm('#theme-selector')
  373. }
  374. })
  375. </script>
  376. </body>
  377. </html>