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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  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` 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>Exercices (de feuille) de styles (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://blog.professeurjoachim.com/billet/2023-01-05-exercices-de-feuille-de-styles">
  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>Exercices (de feuille) de styles</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://blog.professeurjoachim.com/billet/2023-01-05-exercices-de-feuille-de-styles" title="Lien vers le contenu original">Source originale</a>
  70. </p>
  71. </nav>
  72. <hr>
  73. <p>Ces temps-ci j’ai environ 4 projets persos démarrés (plus 3 à l’état d’ébauche), auxquels j’ajoute un ou deux projets open source auxquels je contribue. C’est pour ça que quand j’ai cinq minutes, je fais des petits trucs sur mon blog, qui n’est pas comptabilisé dans les projets cités plus haut.</p>
  74. <p>L’avantage avec le blog, c’est que la base est là, la baraque est construite, et que je peux juste faire des expériences de style, comme si je repeignais un mur, mais en moins salissant.</p>
  75. <p>Tel qu’il est actuellement, le blog est propulsé par <a href="https://getkirby.com/">Kirby</a>, mon petit chouchou pour la gestion simple de sites qui n’ont pas de gros besoins de fonctionnalités. J’ai un modèle de page d’accueil qui liste les billets du blog, j’ai un modèle de page de billet, et un modèle de page par défaut.</p>
  76. <p>Les styles ont été conçus à partir de zéro. Mes seules règles : que le site soit lisible et accessible, et que je ne m’empèche pas d’utiliser des propriétés CSS modernes.</p>
  77. <p>Parmi les exemples de propriétés modernes, le mode sombre et les liens.</p>
  78. <p>Les couleurs du site sont gérées par le biais de propriétés CSS personnalisées. Une teinte est définie, et déclinée en plusieurs variantes, nommées en fonction du contraste qu’elles ont avec la couleur de fond.</p>
  79. <pre class="hljs"><code data-language="css"><span class="hljs-selector-pseudo">:root</span> {
  80. <span class="hljs-attribute">--color-base</span>: <span class="hljs-number">120deg</span>;
  81. <span class="hljs-attribute">--color-action-low-contrast</span>: <span class="hljs-built_in">hsl</span>(var(--color-base), <span class="hljs-number">80%</span>, <span class="hljs-number">80%</span>);
  82. <span class="hljs-attribute">--color-action-medium-contrast</span>: <span class="hljs-built_in">hsl</span>(var(--color-base), <span class="hljs-number">70%</span>, <span class="hljs-number">40%</span>);
  83. <span class="hljs-attribute">--color-action-high-contrast</span>: <span class="hljs-built_in">hsl</span>(var(--color-base), <span class="hljs-number">60%</span>, <span class="hljs-number">25%</span>)
  84. }</code></pre>
  85. <p><strong>Le mode sombre</strong> est géré par une media-query spécifique, qui modifie les propriétés des couleurs, sans toucher à la teinte de base.</p>
  86. <pre class="hljs"><code data-language="css"><span class="hljs-keyword">@media</span> screen <span class="hljs-keyword">and</span> (<span class="hljs-attribute">prefers-color-scheme:</span> dark) {
  87. <span class="hljs-selector-pseudo">:root</span> {
  88. <span class="hljs-attribute">--color-action-low-contrast</span>: <span class="hljs-built_in">hsl</span>(var(--color-base), <span class="hljs-number">20%</span>, <span class="hljs-number">25%</span>);
  89. <span class="hljs-attribute">--color-action-medium-contrast</span>: <span class="hljs-built_in">hsl</span>(var(--color-base), <span class="hljs-number">50%</span>, <span class="hljs-number">50%</span>);
  90. <span class="hljs-attribute">--color-action-high-contrast</span>: <span class="hljs-built_in">hsl</span>(var(--color-base), <span class="hljs-number">100%</span>, <span class="hljs-number">80%</span>);
  91. }
  92. }</code></pre>
  93. <p>Pour voir les deux versions, j’ai trouvé cette excellente (et minuscule) extension Firefox, qui permet d’alterner entre le mode clair et le mode sombre, quels que soient les réglages du système ou du navigateur : <a href="https://github.com/Cimbali/toggle-dark-mode">https://github.com/Cimbali/toggle-dark-mode</a>.</p>
  94. <p><strong>Les liens</strong> utilisent des propriétés CSS peu connues, mais très pratiques : <code>text-decoration-thickness</code> qui régit l’épaisseur du trait de décoration (ici un soulignement) et <code>text-underline-offset</code> qui cible le trait de soulignement pour le décaler. Ces deux propriétés sont utilisables sur tous les navigateurs depuis 2021, et elles sont animables via la propriété <code>transition</code>. En animant aussi la couleur du trait de soulignement et celle du texte, <a href="#test-de-lien" id="test-de-lien">on obtient ce résultat</a>. Le seul reproche que je peux faire à cette technique, c’est que je ne peux pas ajouter de marge intérieure au trait (qui est plutôt un bloc coloré qui contient le texte) pour le rendre un peu plus large que le texte qu’il contient.</p>
  95. <h2>Un site en jachère…</h2>
  96. <p>Un blog, ça vit, ça meurt… Je le sais, j’en ai entretenu une demi-douzaine en vingt ans (je viens de vérifier la date d’enregistrement de mon premier nom de domaine : ça dit <code>Creation Date: 2003-01-23T17:55:34Z</code>, j’étais au lycée), et la quasi-totalité ne sont plus entretenus, voire même plus sur le web (hé oui, pour les dix ans de mon premier je l’ai détruit, ce qui lui a valu un petit épitaphe sur le carnet d’un ami).</p>
  97. <p>J’avais envie de présenter le manque d’entretien de ce carnet par un biais amusant et facilement compréhensible. Quand on a un jardin, si on le laisse tranquille plusieurs mois, les herbes folles poussent et les feuilles des arbres couvrent le sol. C’est le temps qui passe, c’est la nature.</p>
  98. <p>Sur ce carnet, c’est pareil. Plus le temps s’écoulera entre deux mises à jour, plus les pages verront des plantes et des feuiles mortes. Si elles deviennent trop nombreuses et gênent à la lecture, <a href="#rateau">il est possible de les ratisser</a>.</p>
  99. <h2>…qui évolue un petit peu selon les saisons</h2>
  100. <p>En restant sur un thème de rythme de la nature, et du fait que ce rythme ne s’applique pas au web, j’ai eu envie d’insérer un autre témoin du temps qui passe. Là, il ne sera pas lié à mon entretien de cet espace web mais plutôt au jour de l’année.</p>
  101. <p>Comme je l’ai abordé plus haut, la couleur est définie au format HSL, ou Hue/Saturation/Lightness, soit <a href="https://fr.wikipedia.org/wiki/Teinte_saturation_luminosit%C3%A9">Teinte/Saturation/Luminosité</a> en français. Grâce à la manière dont j’ai séparé la définition de la teinte dans la définition des couleurs, si je change la valeur de la teinte, les valeurs de saturation et de luminosité ne seront pas touchées. </p>
  102. <p>Les valeurs de teinte sont définies en degré, selon leur angle sur le cercle chromatique. Il y a donc 360 couleurs. Et 365 jours dans l’année. Vous voyez où je veux en venir ?</p>
  103. <p>En arrondissant un peu, chaque jour peut avoir une couleur différente selon son numéro dans l’année. Comment l’avoir ? En PHP c’est facile : <code>&lt;?= date('z') ?&gt;</code> nous fournit la valeur, qu’on colle donc dans une définition de style sur l’élément <code>html</code> : </p>
  104. <pre class="hljs"><code data-language="html"><span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"fr"</span> <span class="hljs-attr">style</span>=<span class="hljs-string">"--color-base: &lt;?= date('z') ?&gt;deg;"</span>&gt;</span></code></pre>
  105. <p>Mais attendez ! Le cercle chromatique partirait du rouge en janvier, colorerait le printemps en jaune, l’été en vert puis bleu, et l’automne en violet. J’ai ajusté ça en inversant la valeur du jour, et en choissant un point de départ différent. Comme ça on a bleu (hiver), vert (printemps), orange (été) rouge et violet (automne). Ça se tient mieux, à mon avis.</p>
  106. <p>Et comme je pouvais le faire, j’ai ajouté un filtre à l’icône de raccourci, qui dépend lui aussi de la date actuelle, pour décaler la teinte de l’icône et donc adapter sa couleur en fonction de la couleur principale du blog. L’emoji qui précède le titre du site a lui aussi reçu ce traitement lorsqu’on ne passe pas la souris sur le titre.</p>
  107. <h2>Avec des emoji qui dépassent un peu</h2>
  108. <p>En parlant de l’emoji qui précède le titre, là aussi il y a quelques trucs CSS pour que ça fonctionne.</p>
  109. <p>L’emoji est placé par rapport au positionnement du titre, en retrait par rapport à la colonne de contenu. Jusque là tout va bien. Mais initialement, elle dépassait aussi sur format mobile—et était coupée par le bord de l’écran. Il a fallu trouver une solution.</p>
  110. <p>La colonne de contenu est définie sans avoir besoin de <code>media-query</code>. En effet, ma définition se fonde sur la valeur minimale entre 86% de la largeur de la fenêtre, et la largeur maximum de ligne, qui est de 44 hauteurs de caractère à la taille par défaut. Soit, en CSS :</p>
  111. <pre class="hljs"><code data-language="css"><span class="hljs-selector-pseudo">:root</span> {
  112. <span class="hljs-attribute">--max-body-width</span>: <span class="hljs-number">86vw</span>;
  113. <span class="hljs-attribute">--max-line-length</span>: <span class="hljs-number">44rem</span>;
  114. }
  115. <span class="hljs-selector-tag">body</span> {
  116. <span class="hljs-attribute">width</span>: <span class="hljs-built_in">min</span>(var(--max-body-width), <span class="hljs-built_in">var</span>(--max-line-length));
  117. }</code></pre>
  118. <p>Pour rentrer le titre (et son icône) dans la colonne, il faut ajouter une marge :</p>
  119. <ul>
  120. <li>qui ne soit jamais supérieure à la largeur de l’icône (pour ne pas trop décaler le titre vers la droite)</li>
  121. <li>qui ne soit jamais négative (à l’inverse, pour ne pas décaler le titre vers la droite)</li>
  122. <li>qui dépende de la différence entre la largeur maximale de la ligne et la largeur courante de l’écran (cette valeur est négative en écran large et positive en écran mobile)</li>
  123. </ul>
  124. <p>Ce qui donne, en CSS :</p>
  125. <pre class="hljs"><code data-language="css"><span class="hljs-selector-tag">h1</span> {
  126. <span class="hljs-attribute">--icon-width</span>: <span class="hljs-number">1.25em</span>;
  127. <span class="hljs-attribute">margin-left</span>:
  128. <span class="hljs-built_in">min</span>(
  129. var(--icon-width),
  130. <span class="hljs-built_in">max</span>(
  131. <span class="hljs-number">0px</span>, calc(var(--max-line-length) - <span class="hljs-built_in">var</span>(--max-body-width))
  132. )
  133. );
  134. }</code></pre>
  135. <p>Et voilà, suite à ça j’ai pas trop de conclusion à apporter à l’article. Si ça se trouve je recommencerai tout le travail de style dans trois semaines, ou dans trois ans. Ça va dépendre de mon avancée sur mes autres projets, je pense. D’ici là, on va essayer de ne pas laisser pousser trop d’herbes folles.</p>
  136. </article>
  137. <hr>
  138. <footer>
  139. <p>
  140. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  141. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
  142. </svg> Accueil</a> •
  143. <a href="/david/log/" title="Accès au flux RSS"><svg class="icon icon-rss2">
  144. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-rss2"></use>
  145. </svg> Suivre</a> •
  146. <a href="http://larlet.com" title="Go to my English profile" data-instant><svg class="icon icon-user-tie">
  147. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-user-tie"></use>
  148. </svg> Pro</a> •
  149. <a href="mailto:david%40larlet.fr" title="Envoyer un courriel"><svg class="icon icon-mail">
  150. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-mail"></use>
  151. </svg> Email</a> •
  152. <abbr class="nowrap" title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340"><svg class="icon icon-hammer2">
  153. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-hammer2"></use>
  154. </svg> Légal</abbr>
  155. </p>
  156. <template id="theme-selector">
  157. <form>
  158. <fieldset>
  159. <legend><svg class="icon icon-brightness-contrast">
  160. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-brightness-contrast"></use>
  161. </svg> Thème</legend>
  162. <label>
  163. <input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
  164. </label>
  165. <label>
  166. <input type="radio" value="dark" name="chosen-color-scheme"> Foncé
  167. </label>
  168. <label>
  169. <input type="radio" value="light" name="chosen-color-scheme"> Clair
  170. </label>
  171. </fieldset>
  172. </form>
  173. </template>
  174. </footer>
  175. <script src="/static/david/js/instantpage-5.1.0.min.js" type="module"></script>
  176. <script>
  177. function loadThemeForm(templateName) {
  178. const themeSelectorTemplate = document.querySelector(templateName)
  179. const form = themeSelectorTemplate.content.firstElementChild
  180. themeSelectorTemplate.replaceWith(form)
  181. form.addEventListener('change', (e) => {
  182. const chosenColorScheme = e.target.value
  183. localStorage.setItem('theme', chosenColorScheme)
  184. toggleTheme(chosenColorScheme)
  185. })
  186. const selectedTheme = localStorage.getItem('theme')
  187. if (selectedTheme && selectedTheme !== 'undefined') {
  188. form.querySelector(`[value="${selectedTheme}"]`).checked = true
  189. }
  190. }
  191. const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
  192. window.addEventListener('load', () => {
  193. let hasDarkRules = false
  194. for (const styleSheet of Array.from(document.styleSheets)) {
  195. let mediaRules = []
  196. for (const cssRule of styleSheet.cssRules) {
  197. if (cssRule.type !== CSSRule.MEDIA_RULE) {
  198. continue
  199. }
  200. // WARNING: Safari does not have/supports `conditionText`.
  201. if (cssRule.conditionText) {
  202. if (cssRule.conditionText !== prefersColorSchemeDark) {
  203. continue
  204. }
  205. } else {
  206. if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
  207. continue
  208. }
  209. }
  210. mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
  211. }
  212. // WARNING: do not try to insert a Rule to a styleSheet you are
  213. // currently iterating on, otherwise the browser will be stuck
  214. // in a infinite loop…
  215. for (const mediaRule of mediaRules) {
  216. styleSheet.insertRule(mediaRule.cssText)
  217. hasDarkRules = true
  218. }
  219. }
  220. if (hasDarkRules) {
  221. loadThemeForm('#theme-selector')
  222. }
  223. })
  224. </script>
  225. </body>
  226. </html>