{"id":639,"date":"2024-01-23T16:41:42","date_gmt":"2024-01-23T22:41:42","guid":{"rendered":"https:\/\/frontendmasters.com\/blog\/?p=639"},"modified":"2024-01-23T16:41:42","modified_gmt":"2024-01-23T22:41:42","slug":"highlight-text-when-a-user-scrolls-down-to-that-piece-of-text","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/highlight-text-when-a-user-scrolls-down-to-that-piece-of-text\/","title":{"rendered":"Highlight Text When a User Scrolls Down to That Piece of Text"},"content":{"rendered":"\n<p><a href=\"https:\/\/www.lenesaile.com\/en\/blog\/about-subgrid-and-colored-grid-lines\/\">I was reading a great post on Lene Saile&#8217;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&#8217;s a video so you can see what I mean:<\/p>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player\" style=\"\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe title=\"VideoPress Video Player\" aria-label='VideoPress Video Player' width='500' height='369' src='https:\/\/videopress.com\/embed\/g8oFdeta?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=1&amp;persistVolume=0&amp;playsinline=1&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0' frameborder='0' allowfullscreen data-resize-to-parent=\"true\" allow='clipboard-write'><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1674852142'><\/script><\/div>\n\t\t\t\n\t\t\t\n\t\t<\/figure>\n\t\t\n\n\n<p>The highlighted line is done with a <code>&lt;mark><\/code> element in HTML, which feels right. I noticed the class name on Lene&#8217;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&#8217;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&#8217;m hip to Scroll-Driven Animations<\/a>.  <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Basic HTML<\/h2>\n\n\n\n<p>A paragraph with a mark (with a class):<\/p>\n\n\n<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>\n\n\n<h2 class=\"wp-block-heading\">CSS Mark Styling in CSS<\/h2>\n\n\n\n<p>I just want a solid background on the mark. But I&#8217;m <em>not<\/em> going to use <code>background-color<\/code>. Instead I&#8217;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>\n\n\n<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> {\n  <span class=\"hljs-attribute\">background-size<\/span>: <span class=\"hljs-number\">100%<\/span> <span class=\"hljs-number\">100%<\/span>;\n  <span class=\"hljs-attribute\">background-repeat<\/span>: no-repeat;\n  <span class=\"hljs-attribute\">background-color<\/span>: transparent;\n  <span class=\"hljs-attribute\">background-image<\/span>: <span class=\"hljs-built_in\">linear-gradient<\/span>(purple, purple);\n}<\/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>\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"322\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/01\/Screenshot-2024-01-23-at-2.20.48%E2%80%AFPM.png?resize=1024%2C322&#038;ssl=1\" alt=\"\" class=\"wp-image-643\" srcset=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/01\/Screenshot-2024-01-23-at-2.20.48%E2%80%AFPM.png?resize=1024%2C322&amp;ssl=1 1024w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/01\/Screenshot-2024-01-23-at-2.20.48%E2%80%AFPM.png?resize=300%2C94&amp;ssl=1 300w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/01\/Screenshot-2024-01-23-at-2.20.48%E2%80%AFPM.png?resize=768%2C241&amp;ssl=1 768w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/01\/Screenshot-2024-01-23-at-2.20.48%E2%80%AFPM.png?resize=1536%2C483&amp;ssl=1 1536w, https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/01\/Screenshot-2024-01-23-at-2.20.48%E2%80%AFPM.png?w=1622&amp;ssl=1 1622w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/figure>\n\n\n\n<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&#8217;re after. It even works when the text breaks across lines which is a miracle.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"187\" 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&#038;ssl=1\" alt=\"\" class=\"wp-image-644\"\/><\/figure>\n\n\n\n<p>Now it&#8217;s a matter of <em>when<\/em> to run the animation. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Scroll-Driven Animation for the Mark<\/h2>\n\n\n\n<p>Here&#8217;s the whole trick:<\/p>\n\n\n<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> {\n  <span class=\"hljs-attribute\">background-size<\/span>: <span class=\"hljs-number\">0<\/span> <span class=\"hljs-number\">100%<\/span>;\n  <span class=\"hljs-attribute\">background-repeat<\/span>: no-repeat;\n  <span class=\"hljs-attribute\">background-color<\/span>: transparent;\n  <span class=\"hljs-attribute\">background-image<\/span>: <span class=\"hljs-built_in\">linear-gradient<\/span>(purple, purple);\n\n  <span class=\"hljs-attribute\">animation<\/span>: mark-it linear;\n  <span class=\"hljs-attribute\">animation-fill-mode<\/span>: forwards;\n  <span class=\"hljs-attribute\">animation-timeline<\/span>: <span class=\"hljs-built_in\">view<\/span>();\n  <span class=\"hljs-attribute\">animation-iteration-count<\/span>: <span class=\"hljs-number\">1<\/span>;\n  <span class=\"hljs-attribute\">animation-range<\/span>: contain <span class=\"hljs-number\">0%<\/span> contain <span class=\"hljs-number\">25%<\/span>;\n}\n\n<span class=\"hljs-keyword\">@keyframes<\/span> mark-it {\n  0% {\n    <span class=\"hljs-attribute\">background-size<\/span>: <span class=\"hljs-number\">0<\/span> <span class=\"hljs-number\">100%<\/span>;\n  }\n  100% {\n    <span class=\"hljs-attribute\">background-size<\/span>: <span class=\"hljs-number\">100%<\/span> <span class=\"hljs-number\">100%<\/span>;\n  }\n}<\/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>\n\n\n<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&#8217;re saying to start the animation as soon as the element is fully within the viewport, then finish when it&#8217;s 25% of the way up the currently visible viewport. <\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"638\" src=\"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/01\/CleanShot-2024-01-23-at-14.29.33%402x.png?resize=1024%2C638&#038;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%402x.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%402x.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%402x.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%402x.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%402x.png?resize=2048%2C1275&amp;ssl=1 2048w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><figcaption class=\"wp-element-caption\">Here the element is maybe 20% up the current viewport, so the animation is 80% finished.<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Demo<\/h2>\n\n\n\n<p>Remember <a href=\"https:\/\/caniuse.com\/mdn-css_properties_animation-range\">native support for Scroll-Driven Animations<\/a> is essentially Chrome &#8216;n&#8217; 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>\n\n\n\n<div class=\"wp-block-cp-codepen-gutenberg-embed-block cp_embed_wrapper\"><iframe id=\"cp_embed_mdoMQay\" src=\"\/\/codepen.io\/anon\/embed\/mdoMQay?height=450&amp;theme-id=47434&amp;slug-hash=mdoMQay&amp;default-tab=result\" height=\"450\" scrolling=\"no\" frameborder=\"0\" allowfullscreen allowpaymentrequest name=\"CodePen Embed mdoMQay\" title=\"CodePen Embed mdoMQay\" class=\"cp_embed_iframe\" style=\"width:100%;overflow:hidden\">CodePen Embed Fallback<\/iframe><\/div>\n\n\n\n<p>Demo video:<\/p>\n\n\n\n\t\t<figure class=\"wp-block-jetpack-videopress jetpack-videopress-player\" style=\"\" >\n\t\t\t<div class=\"jetpack-videopress-player__wrapper\"> <iframe title=\"VideoPress Video Player\" aria-label='VideoPress Video Player' width='500' height='252' src='https:\/\/videopress.com\/embed\/98cURAgj?cover=1&amp;autoPlay=0&amp;controls=1&amp;loop=0&amp;muted=1&amp;persistVolume=0&amp;playsinline=1&amp;preloadContent=metadata&amp;useAverageColor=1&amp;hd=0' frameborder='0' allowfullscreen data-resize-to-parent=\"true\" allow='clipboard-write'><\/iframe><script src='https:\/\/v0.wordpress.com\/js\/next\/videopress-iframe.js?m=1674852142'><\/script><\/div>\n\t\t\t\n\t\t\t\n\t\t<\/figure>\n\t\t","protected":false},"excerpt":{"rendered":"<p>I was reading a great post on Lene Saile&#8217;s blog and noticed a cool little design feature on her site that highlights a line of text once you scroll to it. Here&#8217;s a video so you can see what I mean: The highlighted line is done with a &lt;mark> element in HTML, which feels right. [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":649,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"sig_custom_text":"","sig_image_type":"featured-image","sig_custom_image":0,"sig_is_disabled":false,"inline_featured_image":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[31,86,57],"class_list":["post-639","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-html","tag-mark","tag-scroll-driven-animations"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2024\/01\/scroll-thumb-1.jpg?fit=1000%2C500&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/639","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/comments?post=639"}],"version-history":[{"count":2,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/639\/revisions"}],"predecessor-version":[{"id":650,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/639\/revisions\/650"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/649"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=639"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=639"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=639"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}