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.

преди 8 месеца
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. title: Highlight Text When a User Scrolls Down to That Piece of Text
  2. url: https://frontendmasters.com/blog/highlight-text-when-a-user-scrolls-down-to-that-piece-of-text/
  3. hash_url: 5ff0fe74d43b57fa41db8851bc56ac88
  4. archive_date: 2024-03-12
  5. og_image: https://frontendmasters.com/blog/wp-json/social-image-generator/v1/image/639
  6. description: I was reading a great post on Lene Saile’s blog 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 …
  7. favicon: https://frontendmasters.com/favicon-32x32.png
  8. language: en_US
  9. <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>
  10. <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>
  11. <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>
  12. <p>A paragraph with a mark (with a class):</p>
  13. <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>
  14. <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>
  15. <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>
  16. <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> {
  17. <span class="hljs-attribute">background-size</span>: <span class="hljs-number">100%</span> <span class="hljs-number">100%</span>;
  18. <span class="hljs-attribute">background-repeat</span>: no-repeat;
  19. <span class="hljs-attribute">background-color</span>: transparent;
  20. <span class="hljs-attribute">background-image</span>: <span class="hljs-built_in">linear-gradient</span>(purple, purple);
  21. }</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>
  22. <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>
  23. <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>
  24. <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>
  25. <p>Now it’s a matter of <em>when</em> to run the animation. </p>
  26. <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>
  27. <p>Here’s the whole trick:</p>
  28. <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> {
  29. <span class="hljs-attribute">background-size</span>: <span class="hljs-number">0</span> <span class="hljs-number">100%</span>;
  30. <span class="hljs-attribute">background-repeat</span>: no-repeat;
  31. <span class="hljs-attribute">background-color</span>: transparent;
  32. <span class="hljs-attribute">background-image</span>: <span class="hljs-built_in">linear-gradient</span>(purple, purple);
  33. <span class="hljs-attribute">animation</span>: mark-it linear;
  34. <span class="hljs-attribute">animation-fill-mode</span>: forwards;
  35. <span class="hljs-attribute">animation-timeline</span>: <span class="hljs-built_in">view</span>();
  36. <span class="hljs-attribute">animation-iteration-count</span>: <span class="hljs-number">1</span>;
  37. <span class="hljs-attribute">animation-range</span>: contain <span class="hljs-number">0%</span> contain <span class="hljs-number">25%</span>;
  38. }
  39. <span class="hljs-keyword">@keyframes</span> mark-it {
  40. 0% {
  41. <span class="hljs-attribute">background-size</span>: <span class="hljs-number">0</span> <span class="hljs-number">100%</span>;
  42. }
  43. 100% {
  44. <span class="hljs-attribute">background-size</span>: <span class="hljs-number">100%</span> <span class="hljs-number">100%</span>;
  45. }
  46. }</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>
  47. <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>
  48. <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>
  49. <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>
  50. <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>