Tag: Transitions

SPAs, Shared Element Transitions, and Re-Evaluating Technology

Nolan Lawson sparked some discussion when he described a noticeable shift away from single-page applications (SPAs):

Hip new frameworks like Astro, Qwik, and Elder.js are touting their MPA [multi-page application] with “0kB JavaScript by default.” Blog posts are making the rounds listing all the challenges with SPAs: history, focus management, scroll restoration, Cmd/Ctrl-click, memory leaks, etc. Gleeful potshots are being taken against SPAs.

I think what’s less discussed, though, is how the context has changed in recent years to give MPAs more of an upper hand against SPAs.

It seems a number of folks really clung to that first part because Nolan published a follow-up to clarify that SPAs are far from doomed:

[T]he point of my post wasn’t to bury SPAs and dance on their grave. I think SPAs are great, I’ve worked on many of them, and I think they have a bright future ahead of them. My main point was: if the only reason you’re using an SPA is because “it makes navigations faster,” then maybe it’s time to re-evaluate that.

And there’s good reason he says that. In fact, the first article specifically points to work being done on Shared Element Transitions. If they move forward, we’ll have an API for animating/transitioning/sizing/positioning elements on page entrance and exist. Jake Archibald demonstrated how it works at Google I/O 2022 and the video is a gem.

If you’re wondering how one page can transition into another, the browser takes screenshots of the outgoing page and the incoming page, then transitions between those. So, it’s not so much one page becoming another as much as it is the browser holding onto two images so it can animate one in while the other animates out. Jake says what’s happening behind the scene is a DOM structure is created out of pseudo-elements containing the page images:

<transition-container>   <image-wrapper>     <outgoing-image />     <incoming-image />   </> </>

We can “screenshot” a specific element if we want to isolate it and apply a different animation from the rest of the page:

.site-header {   page-transition-tag: site-header;   contain: paint; }

And we get pseudo-elements we can hook into and assign custom @keyframe animations:

<!-- ::page-transition=container(root)  --> <transition-container>   <!-- ::page-transition-image-wrapper(root)  -->   <image-wrapper>     <!-- ::page-transition-outgoing-image(root) -->     <outgoing-image />     <!-- ::page-transition-incoming-image(root) -->     <incoming-image />   </> </>

Dang, that’s clever as heck!

It’s also proof in the pudding of just how much HTML, CSS, and JavaScript continue to evolve and improve. So much so that Jeremy Keith suggests it’s high time we re-evaluate our past judgment of some technologies:

If you weren’t aware of changes over the past few years, it would be easy to still think that single page apps offer some unique advantages that in fact no longer hold true. […] But developers remain suspicious, still prefering to trust third-party libraries over native browser features. They made a decision about those libraries in the past. They evaluated the state of browser support in the past. I wish they would re-evaluate those decisions.

The ingredients for SPAs specifically:

In recent years in particular it feels like the web has come on in leaps and bounds: service workers, native JavaScript APIs, and an astonishing boost in what you can do with CSS. Most important of all, the interoperability between browsers is getting better and better. Universal support for new web standards arrives at a faster rate than ever before.

HTML, CSS, and JavaScript: it’s still the best cocktail in town. Even if it takes a minute for it to catch up.


SPAs, Shared Element Transitions, and Re-Evaluating Technology originally published on CSS-Tricks. You should get the newsletter.

CSS-Tricks

, , , , ,

CSS Grid Can Do Auto Height Transitions

Bonafide CSS trick alert! Nelson Menezes figured out a new way (that only works in Firefox for now) that is awfully clever.

Perhaps you know that CSS cannot animate to auto dimensions, which is super unfortunate. Animating from zero to “whatever is necessary” would be very helpful very often. We’ve documented the available techniques. They boil down to:

  • Animate the max-height to some more than you need value, which makes timing easing imprecise and janky.
  • Use JavaScript to measure the final size and animate to that, which means… using JavaScript.

Nelson’s technique is neither of those, nor some transform-based way with visual awkwardness. This technique uses CSS Grid at its core…

.expander {   display: grid;   grid-template-rows: 0fr;   transition: grid-template-rows 1s; }  .expander.expanded {   grid-template-rows: 1fr; }

Unbelievably, in Firefox, that transitions content inside that area between 0 and the natural height of the content. There is only a little more to it, like hiding overflow and visibility to make it look right while maintaining accessibility:

That’s wonderful. Let’s get some stars on this issue and maybe Chrome will pick it up. But of course, even better would be if auto height transitions just started working. I can’t imagine that’s totally outside the realm of possibility.


The post CSS Grid Can Do Auto Height Transitions appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , ,
[Top]

Build Complex CSS Transitions using Custom Properties and cubic-bezier()

I recently illustrated how we can achieve complex CSS animations using cubic-bezier() and how to do the same when it comes to CSS transitions. I was able to create complex hover effect without resorting to keyframes. In this article, I will show you how to create even more complex CSS transitions.

This time, let’s use the @property feature. It’s only supported on Chrome-based browsers for now but we can still play with it and demonstrate how it, too, and can be used to build complex animations.

I highly recommend reading my previous article because I will be referring to a few concepts I explained in detail there. Also, please note that the demos in this article are best viewed in Chromium-based browsers while @property support is still limited.

Let’s start with a demo:

Click on the button (more than once) and see the “magic” curve we get. It may look trivial at first glance because we can achieve such effect using some complex keyframes. But the trick is that there is no keyframe in there! That animation is done using only a transition.

Awesome right? And this is only the beginning, so let’s dig in!

The main idea

The trick in the previous example relies on this code:

@property --d1 {   syntax: '<number>';   inherits: false;   initial-value: 0; } @property --d2 {   syntax: '<number>';   inherits: false;   initial-value: 0; }  .box {   top: calc((var(--d1) + var(--d2)) * 1%);   transition:     --d1 1s cubic-bezier(0.7, 1200, 0.3, -1200),     --d2 1s cubic-bezier(0.5, 1200, 0.5, -1200); } .box:hover {   --d1: 0.2;   --d1: -0.2; }

We’re defining two custom properties, --d1 and --d2. Then, we declare the top property on a .box element using the sum of both those properties. Nothing overly complex yet—just calc() applied to two variables.

The two properties are defined as <number> and I multiply those values by 1% to convert them into a percentage. We could define these as <percentage> right away to avoid the multiplication. But I’ve chosen numbers instead in favor of more flexibility for more complex operations later.

Notice that we apply a different transition to each variable—more precisely, a different timing-function with the same duration. It’s actually a different sinusoidal curve for both variables which is something I get deep into in my previous article.

From there, the property values change when the .box is hovered, triggering the animation. But why do we get the result we see in the demo?

It’s all about math. We are adding two functions to create a third one. For --d1, we have a function (let’s call it F1); for --d2 , we have another one (let’s call it F2). That means the value of top is F1 + F2.

An example to better illustrate:

The first two transitions illustrate each variable individually. The third one is the sum of them. Imagine that at in each step of the animation we take the value of both variables and we add them together to get each point along the final curve.

Let’s try another example:

This time, we combine two parabolic curve to get a… well, I don’t know its name it but it’s another complex curve!

This trick is not only limited to the parabolic and sinusoidal curve. It can work with any kind of timing function even if the result won’t always be a complex curve.

This time:

  • --d1 goes from 0 to 30 with an ease-in timing function
  • --d2 goes from 0 to -20 with an ease-out timing function

The result? The top value goes from 0 to 10 (30-20) with a custom timing function (the sum of ease-in and ease-out).

We are not getting a complex transition in this case—it’s more to illustrate the fact that it’s a generic idea not only limited to cubic-bezier().

I think it’s time for an interactive demo.

All you have to do is to adjust a few variables to build your own complex transition. I know cubic-bezier() may be tricky, so consider using this online curve generator and also refer to my previous article.

Here are some examples I made:

As you can see, we can combine two different timing functions (created using cubic-bezier() ) to create a third one, complex enough to achieve a fancy transition. The combinations (and possibilities) are unlimited!

In that last example, I wanted to demonstrate how adding two opposite functions lead to the logical result of a constant function (no transition). Hence, the flat line.

Let’s add more variables!

You thought we’d stop at only two variables? Certainly not! We can extend the logic to N variables. There is no restriction—we define each one with a timing function and sum them up.

An example with three variables:

In most cases, two variables are plenty to create a fancy curve, but it’s neat to know that the trick can be extended to more variables.

Can we subract, multiply and divide variables?

Of course! We can also extend the same idea to consider more operations. We can add, subtract, multiply, divide—and even perform a complex formula between variables.

Here, we’re multiplying values:

We can also use one variable and multiply it by itself to get a quadratic function!

Let’s add more fun in there by introducing min()/max() to simulate an abs() function:

Notice that in the second box we will never get higher than the center point on the y-axis because top is always a positive value. (I added a margin-top to make the center of box the reference for 0.)

I won’t get into all the math, but you can imagine the possibilities we have to create any kind of timing function. All we have to do is to find the right formula either using one variable or combining multiple variables.

Our initial code can be generalized:

@property --d1 { /* we do the same for d2 .. dn */   syntax: '<number>';   inherits: false;   initial-value: i1; /* the initial value can be different for each variable */ }  .box {   --duration: 1s; /* the same duration for all */   property: calc(f(var(--d1),var(--d2), .. ,var(--dn))*[1UNIT]);   transition:     --d1 var(--duration) cubic-bezier( ... ),     --d2 var(--duration) cubic-bezier( ... ),     /* .. */     --dn var(--duration) cubic-bezier( ... ); } .box:hover {   --d1:f1;   --d2:f2;   /* .. */   --dn:f3; }

This is pseudo-code to illustrate the logic:

  1. We use @property to define numeric custom properties, each with an initial value.
  2. Each variable has its own timing function but the same duration.
  3. We define an f function that is the formula used between the variables. The function provides a number that we use to multiply the relevant unit. All this runs in calc() applied to the property.
  4. We update the value of each variable on hover (or toggle, or whatever).

Given this, the property transitions from f(i1,i2,…,in) to f(f1,f2,..,fn) with a custom timing function.

Chaining timing functions

We’ve reached the point where we were able to create a complex timing function by combining basic ones. Let’s try another idea that allow us to have more complex timing function: chaining timing functions together.

The trick is to run the transitions sequentially using the transition-delay property. Let’s look back at the interactive demo and apply a delay to one of the variables:

We are chaining timing functions instead of adding them together for yet another way to create more complex timing functions! Mathematically, it’s still a sum, but since the transitions do not run at the same time, we will be summing a function with a constant, and that simulates the chaining.

Now imagine the case with N variables that we are incrementally delayed. Not only can we create complex transitions this way, but we have enough flexibility to build complex timelines.

Here is a funny hover effect I built using that technique:

You will find no keyframes there. A small action scene is made entirely using one element and a CSS transition.

Here is a realistic pendulum animation using the same idea:

Or, how about a ball that bounces naturally:

Or maybe a ball rolling along a curve:

See that? We just created complex animations without a single keyframe in the code!

That’s a wrap!

I hope you took three key points away from this article and the previous one:

  1. We can get parabolic and sinusoidal curves using cubic-bezier() that allow us to create complex transitions without keyframes.
  2. We can create more curves by combining different timing functions using custom properties and calc().
  3. We can chain the curves using the transition-delay to build a complex timeline.

Thanks to these three features, we have no limits when it comes to creating complex animations.


The post Build Complex CSS Transitions using Custom Properties and cubic-bezier() appeared first on CSS-Tricks. You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , , , ,
[Top]

Shared Element Transitions

I was just Hoping for Better Native Page Transitions, and Bramus commented that Chrome is working on something. Looks like it has some fresh enthusiasm for it, as there is a brand new repo, and you can literally test it in Chrome Canary.

The repo is moved over here, and I love the name. “Shared elements” is clutch here. It’s not just like a slide-out, slide-in, or a star wipe, it’s being able to move individual elements to new places. Shawn pointed out that Sarah’s article makes this ability super clear:

I’ll drop the code snippet from the current README here as its really cool:

<style> #e1, #e2, #e3, #newE1, #newE2, #newE3 {   contain: paint; } </style> ... <script> function handleTransition() {   document.documentTransition.prepare({     rootTransition: "reveal-left",     duration: 300,     sharedElements: [e1, e2, e3]   }).then(() => {     changeBodyBackground();     document.documentTransition.start({ sharedElements: [newE1, newE2, newE3] }).then(       () => console.log("transition finished"));   }); } ... </script>

Note you don’t have to deal with updating the URL or anything, that would just automatically happen (I guess?).

While I was chatting about this, Alex Riviere pointed out to me that pre-Chromium Edge had (proprietary) page transitions:

<meta http-equiv="Page-Enter"       content="RevealTrans(Duration=0.600, Transition=6)">

Whaaaat. Christian Schaefer has a post lamenting what we lost when we lost Trident:

As the name implies, such a filter would smoothly transition the user from page to page upon navigation, instead of having pages appear as abruptly as we are used to. There was an extensive list of transition filters you could choose from by referencing them via number:

Wipe up, wipe down, random dissolve, split horizontal out, etc. No star wipes though, incredibly. And definitely no “shared element transitions”


The post Shared Element Transitions appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, ,
[Top]

Still Hoping for Better Native Page Transitions

It sure would be nice to be able to animate the transition between pages if we want to on the web, at least without resorting to hacks or full-blown architecture choices just to achieve it. Some kind of API that would run stuff (it could integrate with WAAPI?) before the page is unloaded, and then some buddy API that would do the same on the way in.

We do have an onbeforeunload API, but I’m not sure what kind of baggage that has. That, or otherwise, is all possible now, but what I want are purpose-built APIs that help us do it cleanly (understandable functions) and with both performance (working as quickly as clicking links normally does) and accessibility (like focus handling) in mind.

If you’re building a single-page app anyway, you get the freedom to animate between views because the page never reloads. The danger here is that you might pick a single-page app just for this ability, which is what I mean by having to buy into a site architecture just to achieve this. That feels like an unfortunate trade-off, as single-page apps bring a ton of overhead, like tooling and accessibility concerns, that you wouldn’t have otherwise needed.

Without a single page app, you could use something like Turbo and animate.css like this. Or, Adam’s new transition.style, a clip-path() based homage to Daniel Edan’s masterpiece. Maybe if that approach was paired with instant.page it would be as fast as any other internal link click.

There are other players trying to figure this out, like smoothState.js and Swup. The trick being: intercept the action to move to the next page, run the animation first, then load the next page, and animate the new page in. Technically, it slows things down a bit, but you can do it pretty efficiently and the movement adds enough distraction that the perceived performance might even be better.

Ideally, we wouldn’t have to animate the entire page but we could have total control to make more interesting transitions. Heck, I was doing that a decade ago with a page for a musician where clicking around the site just moved things around so that the audio would keep playing (and it was fun).

This would be a great place for the web platform to step in. I remember Jake pushed for this years ago, but I’m not sure if that went anywhere. Then we got portals which are… ok? Those are like if you load an iframe on the page and then animate it to take over the whole page (and update the URL). Not much animation nuance possible there, but you could certainly swipe some pages around or fade them in and out (hey here’s another one: Highway), like jQuery Mobile did back in ancient times.


The post Still Hoping for Better Native Page Transitions appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , , ,
[Top]

An Interactive Guide to CSS Transitions

A wonderful post by Josh that both introduces CSS transitions and covers the nuances for using them effectively. I like the advice about transitioning the position of an element, leaving the original space it occupied alone so it doesn’t result in what he calls “doom flicker.” Six hundred and fifty years ago I created CSS Jitter Man to attempt to explain that idea.

The interactive stuff is really neat and helps explain the concepts. I’m a little jealous that Josh writes in MDX — meaning he’s got Markdown and JSX at his disposal. That means these demos can be little one-off React components. Here’s a thread that Josh did showing off how valuable that can be.

Direct Link to ArticlePermalink


The post An Interactive Guide to CSS Transitions appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, ,
[Top]

How to Create a Realistic Motion Blur with CSS Transitions

Before we delve into making a realistic motion blur in CSS, it’s worth doing a quick dive into what motion blur is, so we can have a better idea of what we’re trying to reproduce.

Have you ever taken a photo of something moving quickly, especially under low light, and it turned into a blurry streak? Or maybe the whole camera shook and the whole shot became a series of streaks? This is motion blur, and it’s a byproduct of how a camera works.

Motion Blur 101

Imagine a camera. It’s got a shutter, a door that opens to let light in, and then closes to stop the light from coming in. From the time it opens, to when it closes, is a single photograph, or a single frame of a moving image.

A blurry man wearing a red shirt on a blue bike speeding through the forest.
Real motion blur in action (Photo: Kevin Erdvig, Unsplash)

If the subject of the frame is moving during the time the shutter is open, we end up taking a photo of an object moving through the frame. On film, this shows up as being a steady smear, with the subject being in an infinite number of places between its starting point to its end. The moving object also ends up being semi-transparent, with parts of the background visible behind it.

What computers do to fake this is model several subframes, and then composite them together at a fraction of the opacity. Putting lots of copies of the same object in slightly different places along the path of motion creates a pretty convincing facsimile of a motion blur.

Video compositing apps tend to have settings for how many subdivisions their motion blur should have. If you set this value really low, you can see exactly how the technique works, like this, a frame of an animation of a simple white dot at four samples per frame:

Four overlapping white opaque circles on a black background.
Four samples per frame.
Twelve overlapping white opaque circles on a black background.
Here is 12 samples per frame.
Thirty-two overlapping white opaque circles on a black background.
And by the time we’re at 32 samples per frame, it’s pretty close to fully real, especially when seen at multiple frames per second.

The number of samples it takes to make a convincing motion blur is entirely relative to the content. Something small with sharp edges that’s moving super fast will need a lot of subframes; but something blurry moving slowly might need only a few. In general, using more will create a more convincing effect.

Doing this in CSS

In order to approximate this effect in CSS, we need to create a ton of identical elements, make them semi-transparent, and offset their animations by a tiny fraction of a second.

First, we’ll set up the base with the animation we want using a CSS transition. We’ll go with a simple black dot, and assign it a transform on hover (or tap, if you’re on mobile). We’ll also animate the border radius and color to show the flexibility of this approach.

Here is the base animation without motion blur:

Now, let’s make 20 identical copies of the black dot, all placed in the exact same place with absolute positioning. Each copy has an opacity of 10%, which is a little more than might be mathematically correct, but I find we need to make them more opaque to look solid enough.

The next step is where the magic happens. We add a slightly-increasing transition-delay value for each clone of our dot object. They’ll all run the exact same animation, but they’ll each be offset by three milliseconds. 

The beauty of this approach is that it creates a pseudo-motion blur effect that works for a ton of different animations. We can throw color changes on there, scaling transitions, odd timings, and the motion blur effect still works.

Using 20 object clones will work for plenty of fast and slow animations, but fewer can still produce a reasonable sense of motion blur. You may need to tweak the number of cloned objects, their opacity, and the amount of transition delay to work with your particular animation. The demo we just looked at has the blur effect slightly overclocked to make it more prominent.


Eventually, with the progress of computer power, I expect that some of the major browsers might start offering this effect natively. Then we can do away with the ridiculousness of having 20 identical objects. In the meantime, this is a reasonable way to approximate a realistic motion blur.


The post How to Create a Realistic Motion Blur with CSS Transitions appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

CSS-Tricks

, , , ,
[Top]

The Power of Named Transitions in Vue

Vue offers several ways to control how an element or component visually appears when inserted into the DOM. Examples can be fading in, sliding in, or other visual effects. Almost all of this functionality is based around a single component: the transition component.

A simple example of this is with a single v-if based on a Boolean. When the Boolean is true, the element appears. When the Boolean is false, the element disappears. Normally, this element would just pop in and out of existence, but with the transition component you can control the visual effect.

<transition>   <div v-if="isVisible">is this visible?</div> </transition>

Several articles have been written that cover the transition component quite well, like articles from Sarah Drasner, Nicolas Udy, and Hassan Djirdeh. Each article covers different aspects of Vue’s transition component in detail. This article will expand on the topic by focusing on one aspect of the transition component; the fact that they can be “named.”

<transition name="fade">   <div v-if="isVisible">is this visible?</div> </transition>

The initial change this attribute offers is that the CSS classes injected onto the element during the transition sequence will be prefixed by the given name. Basically, it would be fade-enter instead of v-enter from the example above. This single attribute can go well beyond this simple option. It can be used to leverage certain features of Vue and CSS which allows for some interesting outcomes.

Another thing to consider is that the name attribute can be bound:

<transition v-bind:name="currentTransition">   <div v-if="isVisible">is this visible?</div> </transition>

In this example, the transition will be named the value currentTransition resolves to. This simple change provides another level of options and features to an app’s animations. With static and dynamic named transitions, a project can have a series of prebuilt transitions ready to apply throughout the entire app, components that can extend existing transitions applied to them, switch a transition being used before or after being applied, allowing users to choose transitions, and control how individual elements of a list transition into place based on the current state of that list.

This article is intended to explore these features and explain how to use them.

What happens when transitions are named?

By default, when a transition component is used, it applies specific classes in a specific sequence to the element. These classes can be leveraged in CSS. Without any CSS, these classes, in essence, do nothing for the element. Therefore, there is a need for CSS of this nature:

.v-enter, .v-leave-to {   opacity: 0; }  .v-enter-active, .v-leave-active {   transition: 0.5s; }

This causes the element to fade in and out with a duration of half a second. A minor change to the transition provides for elegant visual feedback to the user. Still, there is an issue to consider. But first, what’s different with a named transition?

.fade-enter, .fade-leave-to {   opacity: 0; }  .fade-enter-active, .fade-leave-active {   transition: 0.5s; }

Essentially the same CSS but with fade- prefixed instead of v-. This naming addresses the potential issue that can happen when using the default class names of the transition component. The v- prefix makes the classes global in effect, especially if the CSS is placed in the style block of the app’s root level. This would, in effect, make *all* transitions without a name attribute throughout the entire app use the same transition effect. For small apps this may suffice, but in larger, more complex apps, it may lead to undesirable visual effects, as not everything should fade in and out over half a second.

Naming transitions provides a level of control for developers throughout the project as to how different elements or components are inserted or removed visually. It is suggested that all transitions be named — even if there is just one — to establish the habit of doing so. Even if an app has only one transition effect, there may be a need to add a new one at a future point. Having already named existing transitions in the project eases the effort of adding a new one.

Building a collection of transition effects

Naming transitions provides for a simple yet very useful process. A common practice might be to create the transition classes as part of the component that is using them. If another common practice of scoping styles for a component is done, those classes will only be available to that particular component. If two different components have similar transitions in their style blocks, then we are just duplicating code.

So, let’s consider keeping CSS for transitions in the style block of the root of the app, typically the app.vue file. For most of my projects, I place them as the last section of the style block, making them easy to locate for adjustments and additions. Keeping the CSS in this location makes the transition effects available to every use of the transition component throughout the entire app. Here are examples from some of my projects.

.fade-enter, .fade-leave-to { opacity: 0; } .fade-enter-active, .fade-leave-active { transition: 0.5s; }  .slide-enter {   opacity: 0;   transform: scale3d(2, 0.5, 1) translate3d(400px, 0, 0); }  .slide-enter-to { transform: scale3d(1, 1, 1); } .slide-enter-active, .slide-leave-active { transition: 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55); } .slide-leave { transform: scale3d(1, 1, 1); }  .slide-leave-to {   opacity: 0;   transform: scale3d(2, 0.5, 1) translate3d(-400px, 0, 0); }  .rotate-enter { transform: perspective(500px) rotate3d(0, 1, 0, 90deg); } .rotate-enter-active, .rotate-leave-active { transition: 0.5s; } .rotate-leave-to { transform: perspective(500px) rotate3d(0, 1, 0, -90deg); }

There are multiple ways to store these transition classes depending on your preferences and the needs of the project. The first, as mentioned earlier, is to keep it all in the style block of the app.vue file. You can also keep a Sass partial of all the transitions in the project’s assets folder and import it into the app’s style block.

<style lang="scss">   @import "assets/_transitions.scss"; </style>

This method allows for adjustments and additions to the collection of transitions outside of the Vue files. Another benefit of this setup is that such a file can be easily transferred between projects if they share transition effects. If one project gets a new transition, then it’s easy enough to transfer the addition to another project without having to touch main project files.

If you’re using CSS instead of Sass, then you can include the file as a requirement of the project. You can accomplish this by keeping the file in the assets folder of the project and placing a require statement in the main.js file.

require("@/assets/transitions.css");

Another option is keep the transition styles in a static CSS file that can be stored elsewhere, either in the public folder of the project or just on the server itself. Since this is a regular CSS file, no building or deployment would be required — just include a link reference in the index.html file.

<link rel="stylesheet" type="text/css" href="/css/transitions.css">

This file could also potentially be stored in a CDN for all projects to share. Whenever the file is updated, the changes are immediately available everywhere it is referenced. If a new transition name is created, then existing projects can start using the new name as needed.

Now, let’s slow down a minute

While we’re building a collection of transitions to use throughout our project, let’s consider users out there who may not want abrupt animations, or who may want no animations at all. Some people could consider our animations over-the-top and unnecessary, but for some, they can actually cause problems. Some time ago, WebKit introduced the prefers-reduced-motion media query to assist with possible Vestibular Spectrum Disorder issues. Eric Bailey also posted a nice introduction to the media query as well.

In most cases, adding the media query as part of our collection of transitions is quite easy and should be considered. We can either reduce the amount of motion involved in the transition to reduce the negative effects or simply turn them off.

Here’s a simple example from one of my demos below:

.next-enter {   opacity: 0;   transform: scale3d(2, 0.5, 1) translate3d(400px, 0, 0); }  .next-enter-to { transform: scale3d(1, 1, 1); } .next-enter-active, .next-leave-active { transition: 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55); } .next-leave { transform: scale3d(1, 1, 1); }  .next-leave-to {   opacity: 0;   transform: scale3d(2, 0.5, 1) translate3d(-400px, 0, 0); }  /* If animations are reduced at the OS level, use simpler transitions */ @media screen and (prefers-reduced-motion: reduce) {   .next-enter {     opacity: 0;     transform: translate3d(100px, 0, 0);   }    .next-enter-active,   .next-leave-active { transition: 0.5s; }    .next-leave-to {     opacity: 0;     transform: translate3d(-100px, 0, 0);   } }

In that example, I took what was a rather exaggerated transition and made it simpler. The animation is a slide that moves to the left with an elastic ease, then scales down and fades out as it moves away. If someone has the reduce motion preference set, then the animation becomes a much simpler transition with a shorter distance (which gives it a slower velocity) and keeps the fade. If we had wanted to turn them off, then we’d only need to reference the classes with the transition property and set their value to none.

To test this requires finding and selecting a checkbox on your respective OS. On Windows, you will find it in Control Panel > Ease of Access Center > Make the computer easier to see section; look for “Turn off all unnecessary animations (when possible).” On a Mac, look under System Preferences > Accessibility > Display; look for “Reduce motion.” The latest iOS devices have a similar setting under Accessibility as well.

Let’s stay flexible with our transitions collection

With this collection of transitions, there is the potential snag of a lack of flexibility with the effects. For instance, what if one element needs a slightly slower fade time? Let’s say that everything else in the effect can stay the same, only the transition-duration needs to be different. There are ways to adjust for that without having to create a whole new transition name.

The easiest method is to use an inline style directly on the element within the transition component.

<transition name="fade">   <div style="transition-duration: 6s;" v-if="isVisible">this has a different duration</div> </transition>

Such a change can also be done through the various ways Vue offers handling styles and classes.

Let’s say you are using the component element with the is attribute for dynamic components such as this:

<transition name="fade" mode="out-in">   <component :is="currentComponent"></component> </transition>

Even with this dynamic component, we have options to adjust properties of the transition effect. Again, we can apply an inline style on the component element, which will be placed on the root element of the component. The root element also receives the transition classes, so we would directly override their properties.

<transition name="fade" mode="out-in">   <component :is="currentComponent" style="transition-duration: 6s;"></component> </transition>

Another option is to pass in props to our components. That way, the desired changes can be applied through the component’s code to its root element.

<transition name="fade" mode="out-in">   <component :is="currentComponent" duration="6s"></component> </transition>
<template>   <div :style="`transition-duration: $  {duration}`">component one</div> </template>  <script> export default {   name: "component-one",   props: {     duration: String   } }; </script>

We can also override the properties of the transition’s classes inside the component’s style block, especially if it is scoped.

<style scoped>   .fade-enter-active,   .fade-leave-active { transition-duration: 1s; } </style>

In this case, the component will have a fade duration of one second instead of the global duration of half-a-second. We can even take it a step further and have different durations for each side of the sequence.

<style scoped>   .fade-enter-active { transition-duration: 1s; }   .fade-leave-active { transition-duration: 2s; } </style>

Any of the global transition classes can be altered within the component when needed. Although this isn’t quite as flexible as changing the property outside of a class structure, it can still be quite useful in certain circumstances.

As you can see, even with our collection of prebuilt transitions, we still have options for flexibility.

Dynamic transitions

Even after all these interesting things we can do with Vue’s transition component, yet another interesting feature waits to be explored. The name attribute on the transition component can be dynamic in nature, meaning we can change the current transition in use at will.

This means that the transition can be changed to have different animation effects with different situations, based in code. For example, we could have a transition change because of the answer to a question, transitions decided from user interaction, and have a list use different transitions based on the current state of the list itself.

Let’s look into these three examples.

Example 1: Change transition based on an answer

In this example, we have a simple math question that must be answered. Two numbers are randomly selected and we are expected to provide the sum. Then the button is clicked to check the answer against the expected answer. A small notification appears above the equation that indicates whether the answer is true or false. If the answer is correct, the notification is given a transition that suggests a head nodding yes with an up and down animation. If your answer is incorrect, the notification goes side-to-side suggesting a head shaking no.

See the Pen
VueJS Dynamic Transitions: Change Transition Based on an Answer
by Travis Almand (@talmand)
on CodePen.

The logic behind this is not overly complicated, nor is the setup of the transition. Here’s the HTML:

<transition :name="currentTransition">   <div id="notification" :class="response.toString()" v-if="answerChecked">{{ response }}</div> </transition>

Rather simple in nature. We have a bound name on the transition and then a v-if on the notification div. We also apply a true or false class to decorate the notification based on the response.

Here’s the CSS for the transitions:

.positive-enter-active { animation: positive 1s; } @keyframes positive {   0% { transform: translate3d(0, 0, 0); }   25% { transform: translate3d(0, -20px, 0); }   50% { transform: translate3d(0, 20px, 0); }   75% { transform: translate3d(0, -20px, 0); }   100% { transform: translate3d(0, 0, 0); } }  .negative-enter-active { animation: negative 1s; } @keyframes negative {   0% { transform: translate3d(0, 0, 0); }   25% { transform: translate3d(-20px, 0, 0); }   50% { transform: translate3d(20px, 0, 0); }   75% { transform: translate3d(-20px, 0, 0); }   100% { transform: translate3d(0, 0, 0); } }

You’ll see that I’m using CSS animations to accomplish the up-and-down and side-to-side effects.

Here’s some of the JavaScript:

methods: {   randomProblem: function () {     this.a = Math.floor(Math.random() * Math.floor(10));     this.b = Math.floor(Math.random() * Math.floor(10));   },   check: function () {     this.response = this.a + this.b === parseInt(this.answer);     this.answerChecked = true;     this.currentTransition = this.response ? 'positive' : 'negative';   },   reset: function () {     this.answer = null;     this.answerChecked = false;     this.randomProblem();   } }

There’s the randomProblem method that sets up our equation. The check method that decides on which transition effect to use based on comparing the provided answer with the correct answer. Then the simple reset method that just, well, resets everything.

This is just a simple example. Another possible example is having a notification that has two different effects based on whether the notification is important or not. If the message is not overly important, then we can have a subtle animation that doesn’t drive the user’s eyes away from the current task. If it is important, we could use an animation that is more direct in nature in an effort to force the eyes up to the notification.

Example 2: Change transition based on user interaction

Another thing we can build is a carousel of some sort. This could be presentation slides, an image gallery, or a series of instructions. The basic idea is that we have a need to present information to the user in a sequence. In this presentation, the user gets to decide when to proceed and whether to move forward or to go backward.

See the Pen
VueJS Dynamic Transitions: Change Transition Based on User Interaction
by Travis Almand (@talmand)
on CodePen.

This, again, is a rather simple setup. The example, more or less, is a slide presentation type of situation. The two buttons at the bottom shift between two components with a sliding transition. A real project would have more components or perhaps logic to change the contents of the components based on the current slide. This example shall stay simple to demonstrate the idea.

Here’s the HTML:

<transition :name="currentTransition" mode="out-in">   <component :is="slides[currentSlide]"></component> </transition>

You’ll see that we’re merely transitioning whenever the component is switched out by a bound is attribute on the component element.

Here’s the CSS:

.next-enter {   opacity: 0;   transform: scale3d(2, 0.5, 1) translate3d(400px, 0, 0); }  .next-enter-to { transform: scale3d(1, 1, 1); } .next-enter-active, .next-leave-active { transition: 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55); } .next-leave { transform: scale3d(1, 1, 1); }  .next-leave-to {   opacity: 0;   transform: scale3d(2, 0.5, 1) translate3d(-400px, 0, 0); }  .prev-enter {   opacity: 0;   transform: scale3d(2, 0.5, 1) translate3d(-400px, 0, 0); }  .prev-enter-to { transform: scale3d(1, 1, 1); } .prev-enter-active, .prev-leave-active { transition: 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55); } .prev-leave { transform: scale3d(1, 1, 1); }  .prev-leave-to {   opacity: 0;   transform: scale3d(2, 0.5, 1) translate3d(400px, 0, 0); }  /* If animations are reduced at the OS level, use simpler transitions */ @media screen and (prefers-reduced-motion: reduce) {   .next-enter { opacity: 0; transform: translate3d(100px, 0, 0); }   .next-enter-active,   .next-leave-active { transition: 0.5s; }   .next-leave-to { opacity: 0; transform: translate3d(-100px, 0, 0); }      .prev-enter { opacity: 0; transform: translate3d(-100px, 0, 0); }   .prev-enter-active,   .prev-leave-active { transition: 0.5s; }   .prev-leave-to { opacity: 0; transform: translate3d(100px, 0, 0); } }

Here we have two transitions, one for when the user clicks on the “next” button and the other is for the “prev” button. Each essentially slides the component in the appropriate direction with the transform property, but with a few extras to create a kind of squeezing effect for a cartoonish feel. We also make use of prefers-reduced-motion to change the animation to be a simpler fade with a small slide to the side in the appropriate direction.

Now, for the JavaScript:

methods: {   changeSlide: function (dir) {     this.currentSlide = dir === 'next' ? this.currentSlide + 1 : this.currentSlide - 1;     this.currentTransition = dir;   } }

Each button calls the changeSlide method on its click event and passes which direction it represents. Then we have some logic to keep track of what the current slide happens to be. A single line controls which transition to use. Since the “next” button passes “next” as the direction it corresponds to the “next” transition in the CSS. Same for the “prev” button. Each time the user clicks a button, the app automatically knows which transition to use. Thus, we have nice transition effects that provide context as to which direction the user is progressing through the sequence.

Example 3: Change transition based on list state

For our final example, we’ll see how to change transitions based on the current state of a list inside a transition-group component. The idea here is a list to be updated an item at a time with a different transition each time.

See the Pen
VueJS Dynamic Transitions: Change Transition Based on List State
by Travis Almand (@talmand)
on CodePen.

In this example, we are presented with a list of cities on the right and a blank list on the left. As cities are chosen on the right, they fill in the blanks on the left. The first city slides in from above while fading into view. The next cities before the last will slide in either from the right or the left, depending on the previous transition, and the last city slides in from below.

Here’s the HTML:

<transition-group :name="currentListTransition" tag="ul" class="list">   <li v-for="(item, index) in selectedItems" :key="item">{{ item }}</li> </transition-group>

As usual, a rather simple setup. Here are the transitions in CSS:

.top-enter-active, .top-leave-active { transition: 0.5s; } .top-enter, .top-leave-to {   opacity: 0;   transform: translate3d(0, -40px, 0); }  .top-move {   opacity: 0.5;   transition: 0.5s; }  .left-enter-active, .left-leave-active { transition: 0.5s; } .left-enter, .left-leave-to {   opacity: 0;   transform: translate3d(-40px, 0, 0); }  .left-move {   opacity: 0.5;   transition: 0.5s; }  .right-enter-active, .right-leave-active { transition: 0.5s; } .right-enter, .right-leave-to {   opacity: 0;   transform: translate3d(40px, 0, 0); }  .right-move {   opacity: 0.5;   transition: 0.5s; }  .bottom-enter-active, .bottom-leave-active { transition: 0.5s; } .bottom-enter, .bottom-leave-to {   opacity: 0;   transform: translate3d(0, 30px, 0); }  .bottom-move {   opacity: 0.5;   transition: 0.5s; }  /* If animations are reduced at the OS level, turn off transitions */ @media screen and (prefers-reduced-motion: reduce) {   .top-enter-active,   .top-leave-active { transition: none; }   .top-move { transition: none; }   .left-enter-active,   .left-leave-active { transition: none; }   .left-move { transition: none; }   .right-enter-active,   .right-leave-active { transition: none; }   .right-move { transition: none; }   .bottom-enter-active,   .bottom-leave-active { transition: none; }   .bottom-move { transition: none; } }

As you can see, a transition for each possible direction of the cities appearing the blank list.

Now, for our JavaScript:

methods: {   chooseCity: function (index) {     let selectedLength = this.selectedItems.length;     let citiesLength = this.cities.length;     let clt = this.currentListTransition;          if (selectedLength === 0) {       clt = 'top';     } else if (selectedLength > 0 && selectedLength < citiesLength - 1) {       clt = clt === 'top' || clt === 'left' ? 'right' : 'left';     } else if (selectedLength === citiesLength - 1) {       clt = 'bottom';     }          this.currentListTransition = clt;     this.selectedItems.push(this.cities[index]);     document.querySelector(`.city:nth-child($  {index + 1})`).classList.add('selected');   },    clearSelection: function () {     this.currentListTransition = 'right';     this.selectedItems = [];     document.querySelectorAll('.city.selected').forEach(element => {       element.classList.remove('selected');     });   } }

The chooseCity method handles what happens as you choose each city. What we mostly care about is the series of if and if/else statements in the middle of the method. As cities are selected, the logic looks at the current length of the selectedItems array that the selected cities eventually get pushed into. If the length is zero, then that’s the first city, so the transition should have it come in from the top. If the length is between zero and the total number of our cities list, then the transition should be right or left. The new direction used is based on the direction of the previous transition direction. Then, finally, if we’re on the last city to be chosen, it’ll change to the bottom transition. Again we use prefers-reduced-motion, in this case to turn off the transitions altogether.

Another option to change transitions for a list is changing according to the type of items chosen; such as east coast versus west coast cities, each having different transitions. Consider changing the transition based on the current number of items added to the list; for instance, a different transition for every five items.

So long, and thanks for all the transitions

After all these examples and ideas, I hope that you will consider leveraging Vue’s transition component in your own projects. Exploring the possibilities of adding transitions and animations to your apps to provide context and interest for your users. In many cases, additions such as these are rather simple to implement, almost to the point of it being a shame not to add them. Vue offers an exciting and highly useful feature, the transition component, out of the box and I can only encourage its usage.

Cheers.

The post The Power of Named Transitions in Vue appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]

CSS Animations and Transitions in Email

We don’t generally think of CSS animations or transitions inside of email, or really any movement at all outside of an awkward occasional GIF. But there is really no reason you can’t use them inside HTML emails, particularly if you do it in a progressive enhancement-friendly way. Like, you could style a link with a hover state and a shaking animation, but if the animation (or even the hover) doesn’t work, it’s still a functional link. Heck, you can use CSS grid in email, believe it or not.

Jason Rodriguez just wrote Understanding CSS Animations in Email: Transitions and Keyframe Animations that covers some of the possibilities. On the supported list of email clients that support CSS transitions and keyframe animations is Apple Mail, Outlook, and AOL mail, among others.

Other things to look at:

The post CSS Animations and Transitions in Email appeared first on CSS-Tricks.

CSS-Tricks

, ,
[Top]