A place to cache linked articles (think custom and personal wayback machine)
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

index.html 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. <!doctype html><!-- This is a valid HTML5 document. -->
  2. <!-- Screen readers, SEO, extensions and so on. -->
  3. <html lang="fr">
  4. <!-- Has to be within the first 1024 bytes, hence before the <title>
  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>Currying 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="#f0f0ea">
  24. <meta name="msapplication-config" content="/static/david/icons2/browserconfig.xml">
  25. <meta name="theme-color" content="#f0f0ea">
  26. <!-- Documented, feel free to shoot an email. -->
  27. <link rel="stylesheet" href="/static/david/css/style_2020-06-19.css">
  28. <!-- See https://www.zachleat.com/web/comprehensive-webfonts/ for the trade-off. -->
  29. <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>
  30. <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>
  31. <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>
  32. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_regular.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  33. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_bold.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  34. <link rel="preload" href="/static/david/css/fonts/triplicate_t3_italic.woff2" as="font" type="font/woff2" media="(prefers-color-scheme: dark)" crossorigin>
  35. <script type="text/javascript">
  36. function toggleTheme(themeName) {
  37. document.documentElement.classList.toggle(
  38. 'forced-dark',
  39. themeName === 'dark'
  40. )
  41. document.documentElement.classList.toggle(
  42. 'forced-light',
  43. themeName === 'light'
  44. )
  45. }
  46. const selectedTheme = localStorage.getItem('theme')
  47. if (selectedTheme !== 'undefined') {
  48. toggleTheme(selectedTheme)
  49. }
  50. </script>
  51. <meta name="robots" content="noindex, nofollow">
  52. <meta content="origin-when-cross-origin" name="referrer">
  53. <!-- Canonical URL for SEO purposes -->
  54. <link rel="canonical" href="https://www.trysmudford.com/blog/currying-in-css/">
  55. <body class="remarkdown h1-underline h2-underline h3-underline hr-center ul-star pre-tick">
  56. <article>
  57. <header>
  58. <h1>Currying in CSS?</h1>
  59. </header>
  60. <nav>
  61. <p class="center">
  62. <a href="/david/" title="Aller à l’accueil">🏠</a> •
  63. <a href="https://www.trysmudford.com/blog/currying-in-css/" title="Lien vers le contenu original">Source originale</a>
  64. </p>
  65. </nav>
  66. <hr>
  67. <main>
  68. <p>There were lots of interesting discoveries found whilst developing <a href="https://utopia.fyi/">Utopia</a>. This one came when declaring CSS custom properties.</p>
  69. <p>Taking a card as an example, one would often start off with some CSS like this:</p>
  70. <div class="highlight"><pre class="chroma"><code class="language-css" data-lang="css"><span class="p">.</span><span class="nc">card</span> <span class="p">{</span>
  71. <span class="k">padding</span><span class="p">:</span> <span class="mi">40</span><span class="kt">px</span><span class="p">;</span>
  72. <span class="k">background</span><span class="p">:</span> <span class="mh">#FAFAFA</span><span class="p">;</span>
  73. <span class="p">}</span></code></pre></div>
  74. <p>Then extract the values out for re-use across the website:</p>
  75. <div class="highlight"><pre class="chroma"><code class="language-css" data-lang="css"><span class="p">:</span><span class="nd">root</span> <span class="p">{</span>
  76. <span class="nv">--card-padding</span><span class="p">:</span> <span class="mi">40</span><span class="kt">px</span><span class="p">;</span>
  77. <span class="nv">--card-background</span><span class="p">:</span> <span class="mh">#FAFAFA</span><span class="p">;</span>
  78. <span class="p">}</span>
  79. <span class="p">.</span><span class="nc">card</span> <span class="p">{</span>
  80. <span class="k">padding</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">card</span><span class="o">-</span><span class="n">padding</span><span class="p">);</span>
  81. <span class="k">background</span><span class="p">:</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">card</span><span class="o">-</span><span class="n">background</span><span class="p">);</span>
  82. <span class="p">}</span></code></pre></div>
  83. <p>We’ve simply lifted the values out into the <code>:root</code> and replaced them verbatim with their variable counterparts. Nothing contentious to see here.</p>
  84. <p>When working with <a href="https://utopia.fyi/blog/fluid-custom-properties">fluid custom properties</a>, I was desperately trying to refactor the equation to keep it as succinct as possible. It was at this point, I stumbled upon a strange property of custom properties…</p>
  85. <blockquote>
  86. <p>The value after the <code>:</code> in the CSS custom property does not have to be valid CSS.</p>
  87. </blockquote>
  88. <p>It won’t cause any errors, nor invalidate the custom property. It won’t be evaluated in the browser until used, or more specifically, placed in a <code>calc()</code> function.</p>
  89. <h2 id="what-does-this-mean">What does this mean?</h2>
  90. <p>This means we can write concise CSS ‘calculations’ without worrying about wrapping every line in <code>calc()</code>s, and providing its wrapped when we use the property, it will be valid. When you finally run <code>calc()</code>, the browser (appears to) gather all the associated parts of the equation, and run them in one go.</p>
  91. <p>The <code>--f-2</code> declaration below is not ‘valid’ CSS. But when used in the <code>font-size</code> with a <code>calc()</code>, it becomes valid:</p>
  92. <div class="highlight"><pre class="chroma"><code class="language-css" data-lang="css"><span class="p">:</span><span class="nd">root</span> <span class="p">{</span>
  93. <span class="nv">--f-2</span><span class="p">:</span> <span class="p">((</span><span class="mi">2</span> <span class="o">/</span> <span class="mi">16</span> <span class="o">-</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">f</span><span class="o">-</span><span class="n">foot</span><span class="p">))</span> <span class="o">*</span> <span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">f</span><span class="o">-</span><span class="n">hill</span><span class="p">));</span>
  94. <span class="p">}</span>
  95. <span class="nt">body</span> <span class="p">{</span>
  96. <span class="k">font-size</span><span class="p">:</span> <span class="nb">calc</span><span class="p">(</span><span class="nf">var</span><span class="p">(</span><span class="o">--</span><span class="n">f</span><span class="mi">-2</span><span class="p">)</span> <span class="o">*</span> <span class="mi">16</span><span class="p">);</span>
  97. <span class="p">}</span></code></pre></div>
  98. <h2 id="so-what">So what?</h2>
  99. <p>To me, this feels much like the two distinct actions of:</p>
  100. <ol>
  101. <li>Defining a function</li>
  102. <li>Calling that function</li>
  103. </ol>
  104. <p>Which is kind of cool. I don’t know whether there’s more here, but I feel like there probably is.</p>
  105. <p>Some way of currying in CSS, perhaps? I’ve likened currying to <a href="https://www.trysmudford.com/blog/pedalboard/#closures-and-currying">priming ‘code grenades’</a>, then calling them later? Who knows, I’m definitely gonna keep digging to find out.</p>
  106. </main>
  107. </article>
  108. <hr>
  109. <footer>
  110. <p>
  111. <a href="/david/" title="Aller à l’accueil">🏠</a> •
  112. <a href="/david/log/" title="Accès au flux RSS">🤖</a> •
  113. <a href="http://larlet.com" title="Go to my English profile" data-instant>🇨🇦</a> •
  114. <a href="mailto:david%40larlet.fr" title="Envoyer un courriel">📮</a> •
  115. <abbr title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340">🧚</abbr>
  116. </p>
  117. <template id="theme-selector">
  118. <form>
  119. <fieldset>
  120. <legend>Thème</legend>
  121. <label>
  122. <input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
  123. </label>
  124. <label>
  125. <input type="radio" value="dark" name="chosen-color-scheme"> Foncé
  126. </label>
  127. <label>
  128. <input type="radio" value="light" name="chosen-color-scheme"> Clair
  129. </label>
  130. </fieldset>
  131. </form>
  132. </template>
  133. </footer>
  134. <script type="text/javascript">
  135. function loadThemeForm(templateName) {
  136. const themeSelectorTemplate = document.querySelector(templateName)
  137. const form = themeSelectorTemplate.content.firstElementChild
  138. themeSelectorTemplate.replaceWith(form)
  139. form.addEventListener('change', (e) => {
  140. const chosenColorScheme = e.target.value
  141. localStorage.setItem('theme', chosenColorScheme)
  142. toggleTheme(chosenColorScheme)
  143. })
  144. const selectedTheme = localStorage.getItem('theme')
  145. if (selectedTheme && selectedTheme !== 'undefined') {
  146. form.querySelector(`[value="${selectedTheme}"]`).checked = true
  147. }
  148. }
  149. const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
  150. window.addEventListener('load', () => {
  151. let hasDarkRules = false
  152. for (const styleSheet of Array.from(document.styleSheets)) {
  153. let mediaRules = []
  154. for (const cssRule of styleSheet.cssRules) {
  155. if (cssRule.type !== CSSRule.MEDIA_RULE) {
  156. continue
  157. }
  158. // WARNING: Safari does not have/supports `conditionText`.
  159. if (cssRule.conditionText) {
  160. if (cssRule.conditionText !== prefersColorSchemeDark) {
  161. continue
  162. }
  163. } else {
  164. if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
  165. continue
  166. }
  167. }
  168. mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
  169. }
  170. // WARNING: do not try to insert a Rule to a styleSheet you are
  171. // currently iterating on, otherwise the browser will be stuck
  172. // in a infinite loop…
  173. for (const mediaRule of mediaRules) {
  174. styleSheet.insertRule(mediaRule.cssText)
  175. hasDarkRules = true
  176. }
  177. }
  178. if (hasDarkRules) {
  179. loadThemeForm('#theme-selector')
  180. }
  181. })
  182. </script>
  183. </body>
  184. </html>