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 41KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. <!doctype html><!-- This is a valid HTML5 document. -->
  2. <!-- Screen readers, SEO, extensions and so on. -->
  3. <html lang="en">
  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>Naming Variables In CSS (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://jwdallas.com/posts/namingcssvariables/">
  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>Naming Variables In CSS</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://jwdallas.com/posts/namingcssvariables/" 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. <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>
  76. <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>
  77. <h2 id="casing">Casing</h2>
  78. <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>
  79. <h3 id="maybe-camelcase-isnt-so-bad">Maybe camelCase isn’t so bad</h3>
  80. <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>
  81. <h3 id="consider-mixing-kebab-casing-with-camelcasing">Consider mixing kebab-casing with camelCasing</h3>
  82. <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>
  83. <pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>:root</span><span> {</span></span>
  84. <span class="line"><span> /* Harder to scan: */</span></span>
  85. <span class="line"><span> --system-control-accent-color</span><span>: </span><span>blue</span><span>;</span></span>
  86. <span class="line"><span> --system-focus-ring-color</span><span>: </span><span>cadetBlue</span><span>;</span></span>
  87. <span class="line"><span> --system-label-color-quaternary</span><span>: </span><span>lightGray</span><span>;</span></span>
  88. <span class="line"><span> --system-heading-title-font-size</span><span>: </span><span>1.5rem</span><span>;</span></span>
  89. <span class="line"><span> --system-subheading-font-size</span><span>: </span><span>1.2rem</span><span>;</span></span>
  90. <span class="line"><span> --system-caption-font-size</span><span>: </span><span>0.65rem</span><span>;</span></span>
  91. <span class="line"></span>
  92. <span class="line"></span>
  93. <span class="line"><span> /* Easier to scan: */</span></span>
  94. <span class="line"><span> --system-color-controlAccent</span><span>: </span><span>blue</span><span>;</span></span>
  95. <span class="line"><span> --system-color-focusRing</span><span>: </span><span>cadetBlue</span><span>;</span></span>
  96. <span class="line"><span> --system-color-labelQuaternary</span><span>: </span><span>lightGray</span><span>;</span></span>
  97. <span class="line"><span> --system-fontSize-headingTitle</span><span>: </span><span>1.5rem</span><span>;</span></span>
  98. <span class="line"><span> --system-fontSize-subheading</span><span>: </span><span>1.2rem</span><span>;</span></span>
  99. <span class="line"><span> --system-fontSize-caption</span><span>: </span><span>0.65rem</span><span>;</span></span>
  100. <span class="line"><span>}</span></span></code></pre>
  101. <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>
  102. <h2 id="namespacing">Namespacing</h2>
  103. <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>
  104. <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>
  105. <pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>:root</span><span> {</span></span>
  106. <span class="line"><span> /* This variable is defined at the root making it shared</span></span>
  107. <span class="line"><span> globally so having a namespace is useful. */</span></span>
  108. <span class="line"><span> --system-color-labelPrimary</span><span>: </span><span>#000</span><span>;</span></span>
  109. <span class="line"><span>}</span></span>
  110. <span class="line"></span>
  111. <span class="line"><span>quiz-library</span><span> {</span></span>
  112. <span class="line"><span> /* This variable is defined within quiz-library so it’s not</span></span>
  113. <span class="line"><span> shared globally. A namespace of "quizLibrary" would be</span></span>
  114. <span class="line"><span> redundant because the variable is only available within</span></span>
  115. <span class="line"><span> quiz-library elements and their descendents. */</span></span>
  116. <span class="line"><span> --color-questionTitle</span><span>: </span><span>var</span><span>(</span><span>--system-color-labelPrimary</span><span>);</span></span>
  117. <span class="line"><span>}</span></span></code></pre>
  118. <h2 id="value-typing">Value typing</h2>
  119. <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>
  120. <pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>button</span><span> {</span></span>
  121. <span class="line"><span> /* Did they set a font family definition to a font size? */</span></span>
  122. <span class="line"><span> font-size</span><span>: </span><span>var</span><span>(</span><span>--system-elephant</span><span>);</span></span>
  123. <span class="line"><span>}</span></span>
  124. <span class="line"></span>
  125. <span class="line"><span>button</span><span> {</span></span>
  126. <span class="line"><span> /* Clear now that the variable sets a font size. */</span></span>
  127. <span class="line"><span> font-size</span><span>: </span><span>var</span><span>(</span><span>--system-fontSize-elephant</span><span>);</span></span>
  128. <span class="line"><span>}</span></span></code></pre>
  129. <h2 id="names-that-are-descriptive">Names that are descriptive</h2>
  130. <p>There are two fundamental categories of variable names in CSS. Consider these two variables:</p>
  131. <ol>
  132. <li><code>--color-icyBlue</code> (value-based)</li>
  133. <li><code>--color-accent</code> (usage-based)</li>
  134. </ol>
  135. <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>
  136. <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>
  137. <h3 id="where-to-use-value-based-naming">Where to use value-based naming</h3>
  138. <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>
  139. <pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>/* Value-based variables at the global level */</span></span>
  140. <span class="line"><span>:root</span><span> {</span></span>
  141. <span class="line"><span> /* Colors */</span></span>
  142. <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>
  143. <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>
  144. <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>
  145. <span class="line"><span> </span></span>
  146. <span class="line"><span> /* Font Sizes */</span></span>
  147. <span class="line"><span> --system-fontSize-jumbo</span><span>: </span><span>3.052rem</span><span>;</span></span>
  148. <span class="line"><span> --system-fontSize-large</span><span>: </span><span>1.563rem</span><span>;</span></span>
  149. <span class="line"><span> --system-fontSize-small</span><span>: </span><span>0.8rem</span><span>;</span></span>
  150. <span class="line"><span> </span></span>
  151. <span class="line"><span> /* Font Weights */</span></span>
  152. <span class="line"><span> --system-fontWeight-bold</span><span>: </span><span>700</span><span>;</span></span>
  153. <span class="line"><span> --system-fontWeight-medium</span><span>: </span><span>400</span><span>;</span></span>
  154. <span class="line"><span> --system-fontWeight-light</span><span>: </span><span>200</span><span>;</span></span>
  155. <span class="line"><span> </span></span>
  156. <span class="line"><span> /* Durations */</span></span>
  157. <span class="line"><span> --system-duration-presto</span><span>: </span><span>60ms</span><span>;</span></span>
  158. <span class="line"><span> --system-duration-allegro</span><span>: </span><span>125ms</span><span>;</span></span>
  159. <span class="line"><span> --system-duration-andante</span><span>: </span><span>500ms</span><span>;</span></span>
  160. <span class="line"></span>
  161. <span class="line"><span> /* Elevation */</span></span>
  162. <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>
  163. <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>
  164. <span class="line"><span>}</span></span></code></pre>
  165. <h3 id="color-palettes">Color palettes</h3>
  166. <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>
  167. <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>
  168. <pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>:root</span><span> {</span></span>
  169. <span class="line"><span> /* Not very human friendly */</span></span>
  170. <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>
  171. <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>
  172. <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>
  173. <span class="line"></span>
  174. <span class="line"><span> /* Friendlier and easier to understand */</span></span>
  175. <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>
  176. <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>
  177. <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>
  178. <span class="line"><span>}</span></span></code></pre>
  179. <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>
  180. <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>
  181. <pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>button.destructive</span><span> {</span></span>
  182. <span class="line"><span> /* There's a UX bug in our code if this color isn't red</span></span>
  183. <span class="line"><span> but the variable name below is somewhat ambiguous. */</span></span>
  184. <span class="line"><span> color</span><span>: </span><span>var</span><span>(</span><span>--system-color-ferrari</span><span>);</span></span>
  185. <span class="line"><span>}</span></span>
  186. <span class="line"></span>
  187. <span class="line"><span>button.destructive</span><span> {</span></span>
  188. <span class="line"><span> /* Clearer now at a glance that a red color was correctly set */</span></span>
  189. <span class="line"><span> color</span><span>: </span><span>var</span><span>(</span><span>--system-color-ferrariRed</span><span>);</span></span>
  190. <span class="line"><span>}</span></span></code></pre>
  191. <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>
  192. <pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>:root</span><span> {</span></span>
  193. <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>
  194. <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>
  195. <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>
  196. <span class="line"><span>}</span></span></code></pre>
  197. <h3 id="where-to-use-usage-based-naming">Where to use usage-based naming</h3>
  198. <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>
  199. <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>
  200. <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>
  201. <pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>:root</span><span> {</span></span>
  202. <span class="line"><span> /* Color palette defined at root */</span></span>
  203. <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>
  204. <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>
  205. <span class="line"><span>}</span></span>
  206. <span class="line"></span>
  207. <span class="line"><span>body</span><span> {</span></span>
  208. <span class="line"><span> /* Custom property for custom controls */</span></span>
  209. <span class="line"><span> --color-accentColor</span><span>: </span><span>var</span><span>(</span><span>--system-color-bondiBlue</span><span>);</span></span>
  210. <span class="line"><span> /* Reflect it below for native controls */</span></span>
  211. <span class="line"><span> accent-color</span><span>: </span><span>var</span><span>(</span><span>--color-accentColor</span><span>);</span></span>
  212. <span class="line"><span>}</span></span>
  213. <span class="line"></span>
  214. <span class="line"><span>foobar-custom-control</span><span> {</span></span>
  215. <span class="line"><span> /* Define CSS interface allowing the background to be changed */</span></span>
  216. <span class="line"><span> --color-background</span><span>: </span><span>var</span><span>(</span><span>--accentColor</span><span>);</span></span>
  217. <span class="line"><span> /* Implement the above interface */</span></span>
  218. <span class="line"><span> background</span><span>: </span><span>var</span><span>(</span><span>--color-background</span><span>);</span></span>
  219. <span class="line"><span>}</span></span>
  220. <span class="line"></span>
  221. <span class="line"><span>form.tinted</span><span> {</span></span>
  222. <span class="line"><span> /* Define CSS interface for applying tint colors to form */</span></span>
  223. <span class="line"><span> --color-formTint</span><span>: </span><span>var</span><span>(</span><span>--system-color-canaryYellow</span><span>);</span></span>
  224. <span class="line"><span>}</span></span>
  225. <span class="line"></span>
  226. <span class="line"><span>form.tinted</span><span> foobar-custom-control</span><span> {</span></span>
  227. <span class="line"><span> /* Utilize the above interface for foobar-custom-control */</span></span>
  228. <span class="line"><span> --color-background</span><span>: </span><span>var</span><span>(</span><span>--color-formTint</span><span>);</span></span>
  229. <span class="line"><span>}</span></span></code></pre>
  230. <h3 id="dark-mode-is-simpler-with-usage-based-variables">Dark mode is simpler with usage-based variables</h3>
  231. <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>
  232. <pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>/* Dark mode WITHOUT usage-based variables… */</span></span>
  233. <span class="line"></span>
  234. <span class="line"><span>:root</span><span> {</span></span>
  235. <span class="line"><span> --system-color-deepBlack</span><span>: </span><span>#333</span><span>;</span></span>
  236. <span class="line"><span> --system-color-offWhite</span><span>: </span><span>#eee</span><span>;</span></span>
  237. <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>
  238. <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>
  239. <span class="line"><span>}</span></span>
  240. <span class="line"></span>
  241. <span class="line"><span>/* Because the variables above are named in</span></span>
  242. <span class="line"><span> a value-based way we can’t reasonably change</span></span>
  243. <span class="line"><span> their values. Instead we fork our CSS below</span></span>
  244. <span class="line"><span> to use one or the other depending on the</span></span>
  245. <span class="line"><span> root appearance. */</span></span>
  246. <span class="line"></span>
  247. <span class="line"><span>[</span><span>data-appearance</span><span>=</span><span>"light"</span><span>] </span><span>body</span><span> {</span></span>
  248. <span class="line"><span> color</span><span>: </span><span>var</span><span>(</span><span>--system-color-deepBlack</span><span>);</span></span>
  249. <span class="line"><span> background</span><span>: </span><span>var</span><span>(</span><span>--system-color-offWhite</span><span>);</span></span>
  250. <span class="line"><span>}</span></span>
  251. <span class="line"></span>
  252. <span class="line"><span>[</span><span>data-appearance</span><span>=</span><span>"dark"</span><span>] </span><span>body</span><span> {</span></span>
  253. <span class="line"><span> color</span><span>: </span><span>var</span><span>(</span><span>--system-color-offWhite</span><span>);</span></span>
  254. <span class="line"><span> background</span><span>: </span><span>var</span><span>(</span><span>--system-color-deepBlack</span><span>);</span></span>
  255. <span class="line"><span>}</span></span>
  256. <span class="line"></span>
  257. <span class="line"><span>[</span><span>data-appearance</span><span>=</span><span>"light"</span><span>] </span><span>a</span><span> {</span></span>
  258. <span class="line"><span> color</span><span>: </span><span>var</span><span>(</span><span>--system-color-deepBlue</span><span>);</span></span>
  259. <span class="line"><span>}</span></span>
  260. <span class="line"></span>
  261. <span class="line"><span>[</span><span>data-appearance</span><span>=</span><span>"dark"</span><span>] </span><span>a</span><span> {</span></span>
  262. <span class="line"><span> color</span><span>: </span><span>var</span><span>(</span><span>--system-color-skyBlue</span><span>);</span></span>
  263. <span class="line"><span>}</span></span></code></pre>
  264. <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>
  265. <pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>/* Dark mode WITH usage-based variables… */</span></span>
  266. <span class="line"></span>
  267. <span class="line"><span>[</span><span>data-appearance</span><span>=</span><span>"light"</span><span>] {</span></span>
  268. <span class="line"><span> --system-color-textPrimary</span><span>: </span><span>#333</span><span>;</span></span>
  269. <span class="line"><span> --system-color-fillPrimary</span><span>: </span><span>#eee</span><span>;</span></span>
  270. <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>
  271. <span class="line"><span>}</span></span>
  272. <span class="line"><span>[</span><span>data-appearance</span><span>=</span><span>"dark"</span><span>] {</span></span>
  273. <span class="line"><span> --system-color-textPrimary</span><span>: </span><span>#eee</span><span>;</span></span>
  274. <span class="line"><span> --system-color-fillPrimary</span><span>: </span><span>#333</span><span>;</span></span>
  275. <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>
  276. <span class="line"><span>}</span></span>
  277. <span class="line"></span>
  278. <span class="line"><span>/* With usage-specific variable names we can</span></span>
  279. <span class="line"><span> change the values for the uses they describe</span></span>
  280. <span class="line"><span> at a very high level of abstraction allowing</span></span>
  281. <span class="line"><span> the lower level code that uses the variables</span></span>
  282. <span class="line"><span> not to need to understand the current</span></span>
  283. <span class="line"><span> appearance mode. */</span></span>
  284. <span class="line"></span>
  285. <span class="line"><span>body</span><span> {</span></span>
  286. <span class="line"><span> color</span><span>: </span><span>var</span><span>(</span><span>--system-color-textPrimary</span><span>);</span></span>
  287. <span class="line"><span> background</span><span>: </span><span>var</span><span>(</span><span>--system-color-fillPrimary</span><span>);</span></span>
  288. <span class="line"><span>}</span></span>
  289. <span class="line"></span>
  290. <span class="line"><span>a</span><span> {</span></span>
  291. <span class="line"><span> color</span><span>: </span><span>var</span><span>(</span><span>--system-color-link</span><span>);</span></span>
  292. <span class="line"><span>}</span></span></code></pre>
  293. <h3 id="levels-of-hierarchy-within-usage-based-variables">Levels of hierarchy within usage-based variables</h3>
  294. <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>
  295. <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>
  296. <pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>:root</span><span> {</span></span>
  297. <span class="line"><span> --system-color-backgroundPrimary</span><span>: </span><span>#fff</span><span>;</span></span>
  298. <span class="line"><span> --system-color-backgroundSecondary</span><span>: </span><span>#eee</span><span>;</span></span>
  299. <span class="line"><span>}</span></span></code></pre>
  300. <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>
  301. <pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>/* Do not do this */</span></span>
  302. <span class="line"></span>
  303. <span class="line"><span>:root</span><span> {</span></span>
  304. <span class="line"><span> /* What is this for? */</span></span>
  305. <span class="line"><span> --system-color-box</span><span>: </span><span>var</span><span>(</span><span>--system-color-neonBlue</span><span>);</span></span>
  306. <span class="line"><span>}</span></span>
  307. <span class="line"></span>
  308. <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>
  309. <span class="line"><span> /* Was this variable used correctly? */</span></span>
  310. <span class="line"><span> background</span><span>: </span><span>var</span><span>(</span><span>--system-color-box</span><span>);</span></span>
  311. <span class="line"><span>}</span></span></code></pre>
  312. <pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>/* Do this instead */</span></span>
  313. <span class="line"></span>
  314. <span class="line"><span>:root</span><span> {</span></span>
  315. <span class="line"><span> /* Clear what this is for */</span></span>
  316. <span class="line"><span> --system-color-focusRing</span><span>: </span><span>var</span><span>(</span><span>--system-color-neonBlue</span><span>);</span></span>
  317. <span class="line"><span>}</span></span>
  318. <span class="line"></span>
  319. <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>
  320. <span class="line"><span> /* Clear it was used correctly */</span></span>
  321. <span class="line"><span> outline-color</span><span>: </span><span>var</span><span>(</span><span>--system-color-focusRing</span><span>);</span></span>
  322. <span class="line"><span>}</span></span></code></pre>
  323. <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>
  324. <pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>/* Do not do this */</span></span>
  325. <span class="line"></span>
  326. <span class="line"><span>:root</span><span> {</span></span>
  327. <span class="line"><span> /* This can only be used in one place meaning its</span></span>
  328. <span class="line"><span> existence needlessly adds complexity to the project */</span></span>
  329. <span class="line"><span> --system-color-mainToolbarBackground</span><span>: </span><span>#eee</span><span>;</span></span>
  330. <span class="line"><span>}</span></span>
  331. <span class="line"></span>
  332. <span class="line"><span>main</span><span> .toolbar</span><span> {</span></span>
  333. <span class="line"><span> background</span><span>: </span><span>var</span><span>(</span><span>--system-color-mainToolbarBackground</span><span>);</span></span>
  334. <span class="line"><span>}</span></span></code></pre>
  335. <pre class="astro-code light-plus" tabindex="0"><code><span class="line"><span>/* Do this instead */</span></span>
  336. <span class="line"></span>
  337. <span class="line"><span>:root</span><span> {</span></span>
  338. <span class="line"><span> /* This name is general enough in scope that it</span></span>
  339. <span class="line"><span> can be used across all UI so it is useful at</span></span>
  340. <span class="line"><span> the global level. */</span></span>
  341. <span class="line"><span> --system-color-backgroundSecondary</span><span>: </span><span>#eee</span><span>;</span></span>
  342. <span class="line"><span>}</span></span>
  343. <span class="line"></span>
  344. <span class="line"><span>main</span><span> .toolbar</span><span> {</span></span>
  345. <span class="line"><span> background</span><span>: </span><span>var</span><span>(</span><span>--system-color-backgroundSecondary</span><span>);</span></span>
  346. <span class="line"><span>}</span></span></code></pre>
  347. <h2 id="wrapping-up">Wrapping up</h2>
  348. <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>
  349. </article>
  350. <hr>
  351. <footer>
  352. <p>
  353. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  354. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
  355. </svg> Accueil</a> •
  356. <a href="/david/log/" title="Accès au flux RSS"><svg class="icon icon-rss2">
  357. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-rss2"></use>
  358. </svg> Suivre</a> •
  359. <a href="http://larlet.com" title="Go to my English profile" data-instant><svg class="icon icon-user-tie">
  360. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-user-tie"></use>
  361. </svg> Pro</a> •
  362. <a href="mailto:david%40larlet.fr" title="Envoyer un courriel"><svg class="icon icon-mail">
  363. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-mail"></use>
  364. </svg> Email</a> •
  365. <abbr class="nowrap" title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340"><svg class="icon icon-hammer2">
  366. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-hammer2"></use>
  367. </svg> Légal</abbr>
  368. </p>
  369. <template id="theme-selector">
  370. <form>
  371. <fieldset>
  372. <legend><svg class="icon icon-brightness-contrast">
  373. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-brightness-contrast"></use>
  374. </svg> Thème</legend>
  375. <label>
  376. <input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
  377. </label>
  378. <label>
  379. <input type="radio" value="dark" name="chosen-color-scheme"> Foncé
  380. </label>
  381. <label>
  382. <input type="radio" value="light" name="chosen-color-scheme"> Clair
  383. </label>
  384. </fieldset>
  385. </form>
  386. </template>
  387. </footer>
  388. <script src="/static/david/js/instantpage-5.1.0.min.js" type="module"></script>
  389. <script>
  390. function loadThemeForm(templateName) {
  391. const themeSelectorTemplate = document.querySelector(templateName)
  392. const form = themeSelectorTemplate.content.firstElementChild
  393. themeSelectorTemplate.replaceWith(form)
  394. form.addEventListener('change', (e) => {
  395. const chosenColorScheme = e.target.value
  396. localStorage.setItem('theme', chosenColorScheme)
  397. toggleTheme(chosenColorScheme)
  398. })
  399. const selectedTheme = localStorage.getItem('theme')
  400. if (selectedTheme && selectedTheme !== 'undefined') {
  401. form.querySelector(`[value="${selectedTheme}"]`).checked = true
  402. }
  403. }
  404. const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
  405. window.addEventListener('load', () => {
  406. let hasDarkRules = false
  407. for (const styleSheet of Array.from(document.styleSheets)) {
  408. let mediaRules = []
  409. for (const cssRule of styleSheet.cssRules) {
  410. if (cssRule.type !== CSSRule.MEDIA_RULE) {
  411. continue
  412. }
  413. // WARNING: Safari does not have/supports `conditionText`.
  414. if (cssRule.conditionText) {
  415. if (cssRule.conditionText !== prefersColorSchemeDark) {
  416. continue
  417. }
  418. } else {
  419. if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
  420. continue
  421. }
  422. }
  423. mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
  424. }
  425. // WARNING: do not try to insert a Rule to a styleSheet you are
  426. // currently iterating on, otherwise the browser will be stuck
  427. // in a infinite loop…
  428. for (const mediaRule of mediaRules) {
  429. styleSheet.insertRule(mediaRule.cssText)
  430. hasDarkRules = true
  431. }
  432. }
  433. if (hasDarkRules) {
  434. loadThemeForm('#theme-selector')
  435. }
  436. })
  437. </script>
  438. </body>
  439. </html>