{"id":6852,"date":"2021-07-21T15:35:17","date_gmt":"2021-07-21T14:35:17","guid":{"rendered":"https:\/\/www.bronco.co.uk\/our-ideas\/?p=6852"},"modified":"2021-07-21T15:35:17","modified_gmt":"2021-07-21T14:35:17","slug":"exploring-non-destructive-scroll-linked-css-animations","status":"publish","type":"post","link":"https:\/\/www.bronco.co.uk\/our-ideas\/exploring-non-destructive-scroll-linked-css-animations\/","title":{"rendered":"Exploring non-destructive scroll-linked CSS animations"},"content":{"rendered":"\n<p>If by some miracle you\u2019ve read my past <a href=\"https:\/\/www.bronco.co.uk\/our-ideas\/page-loading-effects-with-progressive-enhancement\/\">blog<\/a> <a href=\"https:\/\/www.bronco.co.uk\/our-ideas\/experimenting-with-css-number-counters-with-fallback\/\">posts<\/a> relating to website animation effects, you\u2019ll know that I have an issue with websites that implement solutions that are reliant on JavaScript yet lack any fallback for when this fails.<\/p>\n\n\n\n<p>If you visit these websites and disable JavaScript you\u2019re often left with a blank page. I don\u2019t wish to assume the motives of other developers but I\u2019ve never felt at ease relying on JavaScript for core functionality without any other fallback.<\/p>\n\n\n\n<p>It\u2019s this mindset that led to my previous attempts with animated effects. While both examples do the job intended, they\u2019re not ideal for more versatile scroll-based animations.<\/p>\n\n\n\n<p>Taking what I\u2019d learnt on these experiments I knew I could build something better\u2026<\/p>\n\n\n\n<p class=\"codepen\" data-height=\"400\" data-default-tab=\"css,result\" data-slug-hash=\"rNmzgZy\" data-user=\"keanr\" style=\"height: 400px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;\">\n  <span>See the Pen <a href=\"https:\/\/codepen.io\/keanr\/pen\/rNmzgZy\">\n  Non-destructive Scroll Animations<\/a> by Kean Richmond (<a href=\"https:\/\/codepen.io\/keanr\">@keanr<\/a>)\n  on <a href=\"https:\/\/codepen.io\">CodePen<\/a>.<\/span>\n<\/p>\n<script async src=\"https:\/\/cpwebassets.codepen.io\/assets\/embed\/ei.js\"><\/script>\n\n\n\n<p class=\"has-text-align-center\">Clicking to 0.5x view is recommended<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What\u2019s going on\u2026<\/h2>\n\n\n\n<p>The above is a very simple example showing a few elements fade into view from different X\/Y positions when the user scrolls these into the viewport.<\/p>\n\n\n\n<p>The code for this is incredibly lightweight, mostly because it\u2019s not trying to be a one-size-fits-all solution and because the IntersectionObserver API does a lot of the work for us.<\/p>\n\n\n\n<p>Here\u2019s a rundown of the code highlights\u2026<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Detecting IntersectionObserver<\/h3>\n\n\n\n<p>All our animations rely on the IntersectionObserver API being supported. As we can\u2019t detect this within our CSS file, we append the <code>&lt;body><\/code> tag with the <strong>js&#8211;io<\/strong> class when this is supported.<\/p>\n\n\n\n<p>Positioning this minified version of this code high within our <code>&lt;head><\/code> we&#8217;re able to set the <strong>js&#8211;io<\/strong> class quickly and avoid any elements that shouldn\u2019t appear from flashing onto screen which is possible if using an external file for the script.<\/p>\n\n\n\n<p>By using this class within our CSS selectors that declare our animations, we ensure that browsers that don\u2019t support this feature, or have JavaScript disabled do not render these styles.<\/p>\n\n\n\n<p>Also, as all our JavaScript relating to this functionality is located in the <code>&lt;head><\/code> we reduce the possibility of the animations not running at all due to external files not loading, loading slowly or failing due to errors in the code.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Required classes<\/h3>\n\n\n\n<p>Alongside the <strong>js&#8211;io<\/strong> class we also use <strong>to-animate<\/strong> and <strong>intersect<\/strong> classes.<\/p>\n\n\n\n<p>The <strong>to-animate<\/strong> class is used within our HTML to flag that an element is to be animated into view using IntersectionObserver. Combined with the <strong>js&#8211;io<\/strong> class our CSS selectors set the opacity and transform properties of these elements to their starting positions.<\/p>\n\n\n\n<p>The <strong>intersect<\/strong> class is then added to each element that has the <strong>to-animate<\/strong> class as it is scrolled into the viewport. With this class appended our CSS triggers the transitions that bring the element into view.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Clears observer once run<\/h3>\n\n\n\n<p>With many other animation\/effects libraries you\u2019ll find the animations repeat each time an element enters the viewport. While this effect is delightful once, it\u2019s often annoying the more it\u2019s repeated.<\/p>\n\n\n\n<p>To address this, once we\u2019ve added our <strong>intersect<\/strong> class, triggering the animation, we clear the IntersectionObserver so that it\u2019s no longer observing this particular element. The result is an element that animates once and remains in its standard position until refresh.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Setting intelligent defaults with the opportunity to customise<\/h3>\n\n\n\n<p>We expect 90% of any animations using this code will change the opacity and position of the element using <code>transform:translate();<\/code><\/p>\n\n\n\n<p>Setting these as default we limit the need to specify transitions for every element and by combining custom properties for the transform property, we\u2019re able to reduce the amount of CSS required even further.<\/p>\n\n\n\n<p>Yet, if a developer wishes to utilise more complex transitions, or even switch to keyframe animation they can easily unset our defaults and switch in what animations they require without the need for additional JavaScript to target these elements specifically.<\/p>\n\n\n\n<div class=\"hcb_wrap\"><pre class=\"prism line-numbers lang-css\" data-lang=\"CSS\"><code>@media (prefers-reduced-motion:no-preference){\n\n  .js--io .section--1.intersect {transition-timing-function:cubic-bezier(.175,.885,.32,1.275);}\n  .js--io .section--2.intersect {opacity:0; transition:none; animation: 1.5s linear forwards fadeMotion;}\n\n}\n\n@keyframes fadeMotion {\n  to {opacity:1; transform:translate(0);}\n}<\/code><\/pre><\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Prefers-reduced-motion<\/h3>\n\n\n\n<p>Given how simple it is to employ the <code>prefers-reduced-motion<\/code> media query it would be careless for us to not utilise this.<\/p>\n\n\n\n<p>With fallbacks already in place for older browsers and non-JS users we know that by encasing all our animation CSS within this media query that users with this setting enabled are catered for without any additional work beyond the media query.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Not plug &amp; play<\/h2>\n\n\n\n<p>Unlike other libraries I\u2019ve seen, this code isn\u2019t 100% plug and play. It offers only limited transitions and requires a developer to add additional CSS to customise beyond this.<\/p>\n\n\n\n<p>But what this is, is an incredibly lightweight and fault tolerant way of adding <strong>simple animation effects<\/strong> to your website should you have the desire and ability. <\/p>\n\n\n\n<p>This is by no means complex code, yet providing non-JS fallbacks for these kinds of simple effects seems to be non-existent or just very well hidden. Given how easy it seemed to get to this result makes we wonder if the majority of developers have just given up on progressive enhancement techniques altogether?<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Usurped?<\/h2>\n\n\n\n<p>While this code provides a good solution based on current browser support, in the future I expect that <a href=\"https:\/\/drafts.csswg.org\/scroll-animations-1\/\">scroll-linked animation<\/a> through use of the <code>@scroll-timeline<\/code> rule will succeed many techniques reliant on JavaScript, delivering a truly CSS only solution.<\/p>\n\n\n\n<p>See <a href=\"https:\/\/css-tricks.com\/practical-use-cases-for-scroll-linked-animations-in-css-with-scroll-timelines\/\">https:\/\/css-tricks.com\/practical-use-cases-for-scroll-linked-animations-in-css-with-scroll-timelines\/<\/a> for examples of this in action.<\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>If by some miracle you\u2019ve read my past blog posts relating to website animation effects, you\u2019ll know that I have an issue with websites that implement solutions that are reliant on JavaScript yet lack any fallback for when this fails. If you visit these websites and disable JavaScript you\u2019re often left with a blank page. [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":6853,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[2],"class_list":["post-6852","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-web-and-ux"],"acf":[],"_links":{"self":[{"href":"https:\/\/www.bronco.co.uk\/our-ideas\/wp-json\/wp\/v2\/posts\/6852","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.bronco.co.uk\/our-ideas\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.bronco.co.uk\/our-ideas\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.bronco.co.uk\/our-ideas\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.bronco.co.uk\/our-ideas\/wp-json\/wp\/v2\/comments?post=6852"}],"version-history":[{"count":0,"href":"https:\/\/www.bronco.co.uk\/our-ideas\/wp-json\/wp\/v2\/posts\/6852\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.bronco.co.uk\/our-ideas\/wp-json\/wp\/v2\/media\/6853"}],"wp:attachment":[{"href":"https:\/\/www.bronco.co.uk\/our-ideas\/wp-json\/wp\/v2\/media?parent=6852"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.bronco.co.uk\/our-ideas\/wp-json\/wp\/v2\/categories?post=6852"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}