A place to cache linked articles (think custom and personal wayback machine)
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

index.html 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  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>Highlight Text When a User Scrolls Down to That Piece of Text (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://frontendmasters.com/blog/highlight-text-when-a-user-scrolls-down-to-that-piece-of-text/">
  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>Highlight Text When a User Scrolls Down to That Piece of Text</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://frontendmasters.com/blog/highlight-text-when-a-user-scrolls-down-to-that-piece-of-text/" title="Lien vers le contenu original">Source originale</a>
  70. <br>
  71. Mis en cache le 2024-03-12
  72. </p>
  73. </nav>
  74. <hr>
  75. <p><a href="https://www.lenesaile.com/en/blog/about-subgrid-and-colored-grid-lines/">I was reading a great post on Lene Saile’s blog</a> and noticed a cool little design feature on her site that highlights a line of text once you scroll to it. Here’s a video so you can see what I mean:</p>
  76. <p>The highlighted line is done with a <code>&lt;mark&gt;</code> element in HTML, which feels right. I noticed the class name on Lene’s implementation is <code>.gsap-highlight</code> which implies GSAP is used which has as <a href="https://gsap.com/docs/v3/Plugins/ScrollTrigger/">a great Scroll Trigger plugin</a>. Let’s do this <em>without</em> JavaScript though, especially now that <a href="https://frontendmasters.com/blog/background-size-zooming-with-scroll-driven-animations/">I’m hip to Scroll-Driven Animations</a>. </p>
  77. <h2 class="wp-block-heading" id="toc-1"><a href="#basic-html" aria-hidden="true" class="aal_anchor" id="basic-html"><svg aria-hidden="true" class="aal_svg" version="1.1" viewbox="0 0 16 16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Basic HTML</h2>
  78. <p>A paragraph with a mark (with a class):</p>
  79. <pre class="wp-block-code" aria-describedby="shcb-language-1" data-shcb-language-name="HTML, XML" data-shcb-language-slug="xml"><span><code class="hljs language-xml shcb-wrap-lines"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Lorem, ipsum dolor sit amet <span class="hljs-tag">&lt;<span class="hljs-name">mark</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"scroll-highlight"</span>&gt;</span>consectetur adipisicing elit<span class="hljs-tag">&lt;/<span class="hljs-name">mark</span>&gt;</span>. Magnam voluptas aliquid, distinctio voluptatum neque qui modi. In adipisci ratione id officiis nulla veritatis, porro explicabo illum laudantium iure eius velit!<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span></code></span><small class="shcb-language" id="shcb-language-1"><span class="shcb-language__label">Code language:</span> <span class="shcb-language__name">HTML, XML</span> <span class="shcb-language__paren">(</span><span class="shcb-language__slug">xml</span><span class="shcb-language__paren">)</span></small></pre>
  80. <h2 class="wp-block-heading" id="toc-2"><a href="#css-mark-styling-in-css" aria-hidden="true" class="aal_anchor" id="css-mark-styling-in-css"><svg aria-hidden="true" class="aal_svg" version="1.1" viewbox="0 0 16 16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>CSS Mark Styling in CSS</h2>
  81. <p>I just want a solid background on the mark. But I’m <em>not</em> going to use <code>background-color</code>. Instead I’m going to use <code>background-image</code>, because then I can control the <code>background-size</code> which I ultimately want to animate. So:</p>
  82. <pre class="wp-block-code" aria-describedby="shcb-language-2" data-shcb-language-name="CSS" data-shcb-language-slug="css"><span><code class="hljs language-css"><span class="hljs-selector-tag">mark</span><span class="hljs-selector-class">.scroll-highlight</span> {
  83. <span class="hljs-attribute">background-size</span>: <span class="hljs-number">100%</span> <span class="hljs-number">100%</span>;
  84. <span class="hljs-attribute">background-repeat</span>: no-repeat;
  85. <span class="hljs-attribute">background-color</span>: transparent;
  86. <span class="hljs-attribute">background-image</span>: <span class="hljs-built_in">linear-gradient</span>(purple, purple);
  87. }</code></span><small class="shcb-language" id="shcb-language-2"><span class="shcb-language__label">Code language:</span> <span class="shcb-language__name">CSS</span> <span class="shcb-language__paren">(</span><span class="shcb-language__slug">css</span><span class="shcb-language__paren">)</span></small></pre>
  88. <figure class="wp-block-image size-large"><img decoding="async" src="https://i0.wp.com/frontendmasters.com/blog/wp-content/uploads/2024/01/Screenshot-2024-01-23-at-2.20.48%E2%80%AFPM-1024x322.png?resize=1024%2C322&amp;ssl=1" alt="" class="wp-image-643" data-recalc-dims="1"></figure>
  89. <p>Now I can animate that <code>background-size</code> from <code>0% 100%</code> to <code>100% 100%</code> which is the look we’re after. It even works when the text breaks across lines which is a miracle.</p>
  90. <figure class="wp-block-image size-full"><img decoding="async" src="https://i0.wp.com/frontendmasters.com/blog/wp-content/uploads/2024/01/CleanShot-2024-01-23-at-14.22.33.gif?resize=800%2C187&amp;ssl=1" alt="" class="wp-image-644" data-recalc-dims="1"></figure>
  91. <p>Now it’s a matter of <em>when</em> to run the animation. </p>
  92. <h2 class="wp-block-heading" id="toc-3"><a href="#scroll-driven-animation-for-the-mark" aria-hidden="true" class="aal_anchor" id="scroll-driven-animation-for-the-mark"><svg aria-hidden="true" class="aal_svg" version="1.1" viewbox="0 0 16 16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Scroll-Driven Animation for the Mark</h2>
  93. <p>Here’s the whole trick:</p>
  94. <pre class="wp-block-code" aria-describedby="shcb-language-3" data-shcb-language-name="CSS" data-shcb-language-slug="css"><span><code class="hljs language-css"><span class="hljs-selector-tag">mark</span><span class="hljs-selector-class">.scroll-highlight</span> {
  95. <span class="hljs-attribute">background-size</span>: <span class="hljs-number">0</span> <span class="hljs-number">100%</span>;
  96. <span class="hljs-attribute">background-repeat</span>: no-repeat;
  97. <span class="hljs-attribute">background-color</span>: transparent;
  98. <span class="hljs-attribute">background-image</span>: <span class="hljs-built_in">linear-gradient</span>(purple, purple);
  99. <span class="hljs-attribute">animation</span>: mark-it linear;
  100. <span class="hljs-attribute">animation-fill-mode</span>: forwards;
  101. <span class="hljs-attribute">animation-timeline</span>: <span class="hljs-built_in">view</span>();
  102. <span class="hljs-attribute">animation-iteration-count</span>: <span class="hljs-number">1</span>;
  103. <span class="hljs-attribute">animation-range</span>: contain <span class="hljs-number">0%</span> contain <span class="hljs-number">25%</span>;
  104. }
  105. <span class="hljs-keyword">@keyframes</span> mark-it {
  106. 0% {
  107. <span class="hljs-attribute">background-size</span>: <span class="hljs-number">0</span> <span class="hljs-number">100%</span>;
  108. }
  109. 100% {
  110. <span class="hljs-attribute">background-size</span>: <span class="hljs-number">100%</span> <span class="hljs-number">100%</span>;
  111. }
  112. }</code></span><small class="shcb-language" id="shcb-language-3"><span class="shcb-language__label">Code language:</span> <span class="shcb-language__name">CSS</span> <span class="shcb-language__paren">(</span><span class="shcb-language__slug">css</span><span class="shcb-language__paren">)</span></small></pre>
  113. <p>The coolest part to me is the <code>animation-range</code> which gives us the opportunity to say when to start and end the animation with a solid amount of control. In the code above we’re saying to start the animation as soon as the element is fully within the viewport, then finish when it’s 25% of the way up the currently visible viewport. </p>
  114. <figure class="wp-block-image size-large"><img loading="lazy" decoding="async" src="https://i0.wp.com/frontendmasters.com/blog/wp-content/uploads/2024/01/CleanShot-2024-01-23-at-14.29.33@2x.png?resize=1024%2C638&amp;ssl=1" alt="" class="wp-image-645" srcset="https://i0.wp.com/frontendmasters.com/blog/wp-content/uploads/2024/01/CleanShot-2024-01-23-at-14.29.33@2x.png?resize=1024%2C638&amp;ssl=1 1024w, https://i0.wp.com/frontendmasters.com/blog/wp-content/uploads/2024/01/CleanShot-2024-01-23-at-14.29.33@2x.png?resize=300%2C187&amp;ssl=1 300w, https://i0.wp.com/frontendmasters.com/blog/wp-content/uploads/2024/01/CleanShot-2024-01-23-at-14.29.33@2x.png?resize=768%2C478&amp;ssl=1 768w, https://i0.wp.com/frontendmasters.com/blog/wp-content/uploads/2024/01/CleanShot-2024-01-23-at-14.29.33@2x.png?resize=1536%2C957&amp;ssl=1 1536w, https://i0.wp.com/frontendmasters.com/blog/wp-content/uploads/2024/01/CleanShot-2024-01-23-at-14.29.33@2x.png?resize=2048%2C1275&amp;ssl=1 2048w" sizes="(max-width: 1000px) 100vw, 1000px" data-recalc-dims="1"><figcaption class="wp-element-caption">Here the element is maybe 20% up the current viewport, so the animation is 80% finished.</figcaption></figure>
  115. <h2 class="wp-block-heading" id="toc-4"><a href="#demo" aria-hidden="true" class="aal_anchor" id="demo"><svg aria-hidden="true" class="aal_svg" version="1.1" viewbox="0 0 16 16"><path fill-rule="evenodd" d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z"></path></svg></a>Demo</h2>
  116. <p>Remember <a href="https://caniuse.com/mdn-css_properties_animation-range">native support for Scroll-Driven Animations</a> is essentially Chrome ‘n’ friends only right now. You could easily treat this as a progressive enhancement, wrapping the animation stuff in a <code>@supports (animation-timeline: view()) { }</code> block or however you wanna do it.</p>
  117. </article>
  118. <hr>
  119. <footer>
  120. <p>
  121. <a href="/david/" title="Aller à l’accueil"><svg class="icon icon-home">
  122. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-home"></use>
  123. </svg> Accueil</a> •
  124. <a href="/david/log/" title="Accès au flux RSS"><svg class="icon icon-rss2">
  125. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-rss2"></use>
  126. </svg> Suivre</a> •
  127. <a href="http://larlet.com" title="Go to my English profile" data-instant><svg class="icon icon-user-tie">
  128. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-user-tie"></use>
  129. </svg> Pro</a> •
  130. <a href="mailto:david%40larlet.fr" title="Envoyer un courriel"><svg class="icon icon-mail">
  131. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-mail"></use>
  132. </svg> Email</a> •
  133. <abbr class="nowrap" title="Hébergeur : Alwaysdata, 62 rue Tiquetonne 75002 Paris, +33184162340"><svg class="icon icon-hammer2">
  134. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-hammer2"></use>
  135. </svg> Légal</abbr>
  136. </p>
  137. <template id="theme-selector">
  138. <form>
  139. <fieldset>
  140. <legend><svg class="icon icon-brightness-contrast">
  141. <use xlink:href="/static/david/icons2/symbol-defs-2021-12.svg#icon-brightness-contrast"></use>
  142. </svg> Thème</legend>
  143. <label>
  144. <input type="radio" value="auto" name="chosen-color-scheme" checked> Auto
  145. </label>
  146. <label>
  147. <input type="radio" value="dark" name="chosen-color-scheme"> Foncé
  148. </label>
  149. <label>
  150. <input type="radio" value="light" name="chosen-color-scheme"> Clair
  151. </label>
  152. </fieldset>
  153. </form>
  154. </template>
  155. </footer>
  156. <script src="/static/david/js/instantpage-5.1.0.min.js" type="module"></script>
  157. <script>
  158. function loadThemeForm(templateName) {
  159. const themeSelectorTemplate = document.querySelector(templateName)
  160. const form = themeSelectorTemplate.content.firstElementChild
  161. themeSelectorTemplate.replaceWith(form)
  162. form.addEventListener('change', (e) => {
  163. const chosenColorScheme = e.target.value
  164. localStorage.setItem('theme', chosenColorScheme)
  165. toggleTheme(chosenColorScheme)
  166. })
  167. const selectedTheme = localStorage.getItem('theme')
  168. if (selectedTheme && selectedTheme !== 'undefined') {
  169. form.querySelector(`[value="${selectedTheme}"]`).checked = true
  170. }
  171. }
  172. const prefersColorSchemeDark = '(prefers-color-scheme: dark)'
  173. window.addEventListener('load', () => {
  174. let hasDarkRules = false
  175. for (const styleSheet of Array.from(document.styleSheets)) {
  176. let mediaRules = []
  177. for (const cssRule of styleSheet.cssRules) {
  178. if (cssRule.type !== CSSRule.MEDIA_RULE) {
  179. continue
  180. }
  181. // WARNING: Safari does not have/supports `conditionText`.
  182. if (cssRule.conditionText) {
  183. if (cssRule.conditionText !== prefersColorSchemeDark) {
  184. continue
  185. }
  186. } else {
  187. if (cssRule.cssText.startsWith(prefersColorSchemeDark)) {
  188. continue
  189. }
  190. }
  191. mediaRules = mediaRules.concat(Array.from(cssRule.cssRules))
  192. }
  193. // WARNING: do not try to insert a Rule to a styleSheet you are
  194. // currently iterating on, otherwise the browser will be stuck
  195. // in a infinite loop…
  196. for (const mediaRule of mediaRules) {
  197. styleSheet.insertRule(mediaRule.cssText)
  198. hasDarkRules = true
  199. }
  200. }
  201. if (hasDarkRules) {
  202. loadThemeForm('#theme-selector')
  203. }
  204. })
  205. </script>
  206. </body>
  207. </html>