Loading video...

Video Failed to Load

Go Home

CSS Tip! ✨ You can create these parallax effects and image cross-fades with scroll-driven animations 🤙 img { animation: fade; animation-timeline: view(); mix-blend-mode: plus-lighter } img:last-of-type { animation-direction: reverse; } @​keyframes fade { to { opacity: 0; }} This one's fun! 😁 The trick with the cross-fading image is...

242,205 views • 2 years ago •via X (Twitter)

10 Comments

jhey ▲🐻🎈's profile picture
jhey ▲🐻🎈2 years ago

Here's that @CodePen link! 🚀 You can jam on this demo today in all browsers. There is some JavaScript in place if the scroll-driven animations API isn't supported 🤙 GSAP to the rescue! 🏆

Martin Pitt's profile picture
Martin Pitt2 years ago

For a little history and insight behind cross-fading with plus-lighter for folks, here is @jaffathecake's excellent article behind it:

jhey ▲🐻🎈's profile picture
jhey ▲🐻🎈2 years ago

Good share Martin! 🙏 Almost embedded in my mind from when I started working with View Transitions 😅 The number of times I pinged Jake and it was my own problem with a mix-blend-mode thing 🤦‍♂️ This is the spot from the View Transition docs for people interested 🤙 Jake's blog is also a gold mine of interesting deep dives, etc. 👉

Wes's profile picture
Wes2 years ago

Wow, how it would be in responsive mode?

jhey ▲🐻🎈's profile picture
jhey ▲🐻🎈2 years ago

Ha – Great minds... 🤙

Jan's profile picture
Jan2 years ago

So good. Thanks for tutorial.

niftesty 🫐's profile picture
niftesty 🫐2 years ago

Browser compatibility?

jhey ▲🐻🎈's profile picture
jhey ▲🐻🎈2 years ago

Specifically the API is Chrome only atm. But, the demo will work everywhere as I've filled the functionality with GSAP and wrapped the API stuff in a supports query so it can be ripped out easy 🤙

Fermex95's profile picture
Fermex952 years ago

Lo malo es que no está soportado en todos los navegadores

jhey ▲🐻🎈's profile picture
jhey ▲🐻🎈2 years ago

The API isn't, but the demo is 🤙 Will work in all browsers

Related Videos

CSS Trick 🍏 You can create that Apple-style blowout text effect with CSS scroll animations and fixed positioning ⚡️ .word { animation: blowout; animation-timeline: --section; } @​keyframes blowout { 0%, 95% { background: transparent; } to { transform: translateZ(99vh); background: #000; } } The trick here is to fix the position of some things in your layout and then animate them based on their non-fixed containers ⭐️ The .word is position: fixed within a that has a height of 200vh section { view-timeline: --section; } section:first-of-type { height: 200vh; } Every section has a view-timeline: --section that creates a scoped ViewTimeline for each section's children 🤙 As that first section scrolls out of view, you increase its opacity and move it on the z-axis towards you. That gives it the impression of scaling up 🆙 .word { animation: blowout, fade-in; animation-timing-function: ease-in; animation-fill-mode: both; animation-timeline: --section; animation-range: exit-crossing 10% exit 0%; } You can use the exit-crossing range combined with the exit range to get a timing you like ✨ Definitely experiment with these ranges to see how they play. The timing function creates interesting effects too! At the same time as the text scales up, you need to fade in the video and the text that goes with it. Again, you can use the parent container's ViewTimeline --section p, video { animation: fade-in, fade-out; animation-timing-function: ease-in; animation-fill-mode: both; animation-timeline: --section; } p { animation-range: entry 10% entry 35%, exit 0% exit 25%; } video { animation-range: entry 0% entry 25%, exit 10% exit 35%; } The timings are slightly different here. Again, you can play with them to get something that feels right for you! @​keyframes fade-in { to { opacity: 1; }} @​keyframes fade-out { to { opacity: 0; }} Theoretically, you should be able to do something with animation-direction: reverse/alternate like yesterday's demo video { animation: fade 2 both linear alternate-reverse; } The trick with this effect is all about timing and creating containers that are bigger than the content and using them to drive the animations 🏎️ The scroll-driven signature is a bonus! ✍️ Perhaps a follow-up on that one 🤙 As always, any questions, requests, etc. Hit me up! With this demo, the GreenSock ScrollTrigger code is included where the CSS scroll-driven animations API is not supported 🙌 That means you could use this demo today! CodePen.IO link below! 👇

jhey ʕ•ᴥ•ʔ

270,144 views • 2 years ago

CSS Tip! 🐳 You can add little details like this scale down on scroll effect with scroll-driven animations and some sticky positioning 🤙 section { animation: scale-down; animation-timeline: view(); animation-range: exit; } @​keyframes scale-down { to { scale 0.8; } ] In this smaller example, you can lean into using the position to drive an animation that scales itself down as it leaves the viewport (Seen on the Apple Vision Pro site 🍏) The nice thing here is that if you don't have scroll-driven animations, the user still gets a good experience ✨ So how do you do it? There isn't much to it header { transform-origin: 50% 0%; animation: scale-down both ease-in; animation-timeline: view(); animation-range: exit; view-timeline: --header; } @​keyframes scale-down { to { scale: 0.8 0.8; } } That's it. The layout makes use of position: sticky so that the element stays in the shot whilst you scroll the page. As it leaves the page, it scales down inside the 🫶 The other smol animation here is fading the overlay on the video out 😎 Real easy. You may notice the view-timeline you defined above for the 👀 header { view-timeline: --header; } You have a pseudoelement on the text content of the header that lives inside a header > section::before { background: hsl(0 0% 0% / 0.75); opacity: 1; animation: fade both linear; animation-timeline: --header; animation-range: exit-crossing 0% exit 0%; } @​keyframes fade { to { opacity: 0; } } You use a slightly smaller range on this with exit-crossing to fade it out before you start the scale down animation 🤏 That's it! Thought this smaller example would be easier to grok for people 🙏 It's also covered with JavaScript if you really want it for your sites 🤙 CodePen.IO link below 👇

jhey ʕ•ᴥ•ʔ

146,270 views • 2 years ago

CSS Tip! 🚥 You can create these trending expanding scroll indicators with scroll-driven animations and flex 🤙 .indicator { animation: grow; animation-range: contain calc(50% - var(--size)...; animation-timeline: var(--card); } @​keyframes grow { 50% { flex: 3; }} What's the trick? Put the indicators in a container using flex layout and set a width larger than the number of indicators 😉 .indicators { aspect-ratio: 7 / 1; display: flex; } Importantly, set no gap 🤏 To mimic the gap set a transparent border on each indicator and set the background using padding-box .indicator { background: linear-gradient(#​fff, #​fff) padding-box; border-radius: 50px; border: 4px solid transparent; } Now for the animation. You want to create a view-timeline for each card that moves across 🤙 li:nth-of-type(1) { view-timeline: --one inline; } li:nth-of-type(2) { view-timeline: --two inline; } Make sure they use the inline axis too! The trick is hoisting these view-timeline so the indicators can use them with timeline-scope 👀 .track { timeline-scope: --one, --two, ...; } All that's left is for you to create the animation piece using some calc with the card size ⚡️ .indicator { --size: calc(var(--card-width) * 0.9); animation: grow both linear; animation-range: contain calc(50% - var(--size)) contain calc(50% + var(--size)); } .indicator:nth-of-type(1) { animation-timeline: --one; } .indicator:nth-of-type(2) { animation-timeline: --two; } @​keyframes grow { 50% { flex: 3; }} And there you have it, responsive scroll indicators using CSS scroll-driven animations 😎 Sprinkle a little JavaScript to make them clickable and scroll the the right card ✨ const shift = (event) => { if (event​.target.tagName === "BUTTON") { const index = [...event.target.parentNode.children].indexOf(event​.target); const item = document.querySelector(`li:nth-of-type(${index + 1})`); item.scrollIntoView({ behavior: "smooth", inline: "center" }); } }; As always, any questions or suggestions, let me know. I've put a JavaScript fallback in to use GSAP in browsers that don't have scroll-driven animations 🫶 CodePen.IO link below! 👇

jhey ʕ•ᴥ•ʔ

575,728 views • 2 years ago

CSS Tip! 📜 You can use scroll-driven animations to progressively enhance collapsing a floating call to action 🤏 .cta { animation: shrink; animation-timeline: scroll(); animation-range: 0 100px; } @​keyframes shrink { to { width: 48px; } } That's the gist of it. Use the body scroll position with animation-timeline: scroll(). Define the animation-range as when you have scrolled 100px. There's a little more though 🤓 That would "scrub" the width animation. Ideally, you want to trigger that animation. You could animate a custom property with steps() timing and use that to define the width ✨ @​property --scrub { syntax: ' '; inherits: true; initial-value: 0; } body { animation: scrub both steps(1, end); animation-timeline: scroll(); animation-range: 0 100px; } Then transition the --scrub property on the CTA and use it for the width 🤙 .cta { transition: --scrub 0.2s; width: calc(48px + (120px * (1 - (var(--scrub) / 100)))); } Other animations are a matter of preference and timing. For example, you could then make the hand wave, scale down the size, and then slide a gradient across 😉 They have the same structure and technique as the original concept. Waving the hand? 👋 Run it twice, offset the transform-origin. .hand { animation: wave both linear 2; animation-timeline: scroll(); animation-range: 30vh 50vh; transform-origin: 65% 75%; } @​keyframes wave { 50% { rotate: 20deg; } } How's it progressively enhanced? Wrap everything in a @​supports query and a @​media query. If there isn't support, users still get a good experience. It's a floating action button that's circular and already collapsed 🤙 @​supports(animation-timeline: scroll()) { @​media(prefers-reduced-motion: no-preference) {...} } Definitely have a play with the code. Amazing what we're going to be able to do with CSS alone! 🔥 CodePen.IO link below! 👇

jhey ʕ•ᴥ•ʔ

177,952 views • 2 years ago

CSS Trick! ⚡️ You can use scroll-driven animation with background-attachment to create a dynamic glowing card scroller without JS 🔥 section { animation:vibe; animation-timeline:--list; } @​keyframes vibe { to{--hue:320;}} .glow {background: hsl(var(--hue) 80% 50%);} Here's how! 🤙 You can use the background-attachment trick used in other glow card demos 😎 article { background-attachment: fixed; } The difference here is that you aren't going to update the fixed background position with your pointer this time. It can remain fixed. The magic part is that as you scroll, the background will leave the card that's leaving and enter the card that's entering ✨ For the extra background glow, you can use a fixed pseudo element on the list container itself 💪 Once that's in place, you're only task is to change the color of the background as you scroll 🤔 Create a custom property declaration for the --hue @​property --base { inherits: true; syntax: ' '; initial-value: 0; } Then create an animation that updates this value @​keyframes accent { to { --hue: 320; }} The last piece is hooking it up to scroll and there is a little trick in here 👀 First, you need an inline scroll-timeline on the list ul { scroll-timeline: --list inline; } Then you can use timeline-scope to hoist that scroll-timeline up so a parent can use it. You then animate the custom property on this element and let the value cascade down to the places that need it 🔥 section { timeline-scope: --list; animation: accent both linear; animation-timeline: --list; } For example, the glow uses the --hue this way [data-glow] { background-image: radial-gradient( 150px 150px at 50% 50%, hsl(var(--hue) 100% 70% / 0.25), transparent ); } Lastly, scroll-snap is optional of course but plays nice with the scroll-driven animation demos ✨ The key for that is ul { scroll-snap-type: x mandatory; } li { scroll-snap-align: center; } That's it! Pretty fun trick to play with! 🤓 Any questions, let me know! Should we add it to the video walkthrough list? CodePen.IO link below! 👇

jhey ʕ•ᴥ•ʔ

116,625 views • 2 years ago

Future CSS Tip! 🍏 You can create this Apple-style photo scroller by combining CSS scroll-driven animations and CSS scroll-snap 😍 Peep those changing captions 👀 No JS! img { animation: highlight both linear; animation-timeline: view(inline); 👈 Horizontal animation-range: cover 0% cover 50%; 👈 Finish } @ keyframes highlight { 50% { translate: 0 0; scale: var(--starting-scale); 👈 props opacity: var(--starting-opacity); 👈 } 100% { translate: 0 0; scale: 1; opacity: 1; } } Without the animation support, you get a standard unordered list containing some s 🤙 How do we swap the captions though? The "trick" is to use position: absolute on the figcaption and animate their opacity based on the ViewTimeline of their parent list item 😎 figcaption { animation: show both linear; animation-timeline: --list-item; } @ keyframes show { 0%, 45%, 55%, 100% { opacity: 0; } 50% { opacity: 1; } } li { view-timeline-name: --list-item; view-timeline-axis: inline; 👈 important! } The parent of the scroll track uses position: relative so all the captions sit in the middle even though they are in the right place for the markup 🙌 The last bit is the scroll-snap 🤙 Not much to it at all. Wrap the list and make it scrollable. Then add scroll-snap-type .wrapper { scroll-snap-type: x mandatory; overflow-x: scroll; } Then make sure each list item has scroll-snap-align set on it li { scroll-snap-align: center; } That's it! Pretty cool demo to put together and see how to do this stuff with these APIs 🤓 A lot of cool little tricks to pick up for writing your CSS! ⭐️ CodePen.IO link below! 👇

jhey ʕ•ᴥ•ʔ

234,583 views • 3 years ago

CSS Tip! 🍬 You can create a CSS-only sticky CTA using position: sticky or scroll-driven animations 🤙 .cta { position: sticky; margin-top: 110vh; bottom: 2rem; /* 👈 Stick! */ } This is one way 👀 This first way relies on you setting a layout on the body and putting the CTA in a zero-space part of the layout body { display: grid; grid-template-columns: auto 0; } The children of the body are an element with your content and then the CTA. You could also use display:flex too. .content { flex: 1 0 100%; } .cta { place-self: end; } As you scroll the body, the CTA comes into view and sticks in position 🙌 That's one way. If you want to take it further and do something like flip between showing or not, maybe scale it up, or add some special easing, etc. an animation is another way 📜 First, change the styles for your CTA. Note the translate property that's powered by a custom property .cta { position: fixed; bottom: 2rem; right: 2rem; translate: 0 calc(20vh - (var(--show) * 20vh)); transition: translate 0.875s var(--elastic); } Next you need a custom property that you're going to animate @​property --show { inherits: true; initial-value: 0; syntax: ' '; } Lastly, you animate this value on the body. As the property value changes, the value will trickle down to the CTA @​supports (animation-timeline: scroll()) { body { animation: show-cta both steps(1); animation-timeline: scroll(root); animation-range: 0 10vh; } @​keyframes show-cta { to { --show: 1; } } } Using @​supports you can use this as a progressive enhancement. If scroll-driven animations are supported, use them. Otherwise fallback to using position: sticky 🤙 That's it! As always, any questions or requests, hit me up! 🙏 CodePen.IO link below! 👇

jhey ʕ•ᴥ•ʔ

133,020 views • 2 years ago

CSS in 2024 🤯 You can create a range slider with updating value tooltip and color changing track without using any JavaScript 🤯 ::-webkit-slider-thumb{ view-timeline: --thumb inline; } Scroll animation on the slider thumb that animates a number between the "min" and "max" of the range 😅 @​property --value { syntax: ' '; } @​keyframes sync { to { --value: 100; }} Tie that up to the contain animation-range ⚡️ .control { animation: sync both linear reverse; animation-timeline: --thumb; animation-range: contain; } Hoist the view timeline so the tooltip can use the value too! :root { timeline-scope: --thumb; } Then use it in the counter which goes in the tooltip 😇 .tooltip { counter-reset: val var(--value); } .tooltip::after { content: counter(val); } Then the accent color is based on the value too 🎨 [type=range] { accent-color: hsl(var(--value) 90% 65%); } The magic from the quoted post is using anchor positioning on the range thumb to position that tooltip 👏 Never thought of that when working on the spec for this API. This API lets you tether elements to other elements. Using the pseudo is a clever move! ::-webkit-slider-thumb { anchor-name: --thumb; } .tooltip { position: absolute; anchor-default: --thumb; left: anchor(50%); bottom: calc(anchor(top) + 25%); } The last piece is the little bounce transition... OK. I used a line or two of JavaScript for that 🙏😬 const updateDelta = ({ movementX }) => { document​.documentElement​.style.setProperty('--delta-x', movementX) } document.body.addEventListener('pointermove', updateDelta) But only so you can pass the movement delta to CSS and then reset the position with linear() to get that bouncy transition 😎 .hint { rotate: calc(clamp(-60, var(--delta-x) * -1, 60) * 1deg); transition: rotate 1s linear( 0, 0.2178 2.1%, 1.1144 8.49%, ... ); } Should probably do a video on this one. Lots of little tricks to break down with it for sure! 💯 As always, any questions, let me know! Also, this one only works in Chrome Canary with the Experimental Web Platform Features flag enabled ✅ This one almost feels like rocket science ha 🚀 CodePen.IO link below! 👇

jhey ʕ•ᴥ•ʔ

251,867 views • 2 years ago