Tag: Power

The Power (and Fun) of Scope with CSS Custom Properties

You’re probably already at least a little familiar with CSS variables. If not, here’s a two-second overview: they are really called custom properties, you set them in declaration blocks like --size: 1em and use them as values like font-size: var(--size);, they differ from preprocessor variables (e.g. they cascade), and here’s a guide with way more information.

But are we using them to their full potential? Do we fall into old habits and overlook opportunities where variables could significantly reduce the amount of code we write?

This article was prompted by a recent tweet I made about using CSS variables to create dynamic animation behavior.

Let’s look at a couple of instances where CSS variables can be used to do some pretty cool things that we may not have considered.

Basic scoping wins

The simplest and likely most common example would be scoped colors. And what’s our favorite component to use with color? The button. 😅

Consider the standard setup of primary and secondary buttons. Let’s start with some basic markup that uses a BEM syntax.

<button class="button button--primary">Primary</button> <button class="button button--secondary">Secondary</button>

Traditionally, we might do something like this to style them up:

.button {   padding: 1rem 1.25rem;   color: #fff;   font-weight: bold;   font-size: 1.25rem;   margin: 4px;   transition: background 0.1s ease; }  .button--primary {   background: hsl(233, 100%, 50%);   outline-color: hsl(233, 100%, 80%); }  .button--primary:hover {   background: hsl(233, 100%, 40%); }  .button--primary:active {   background: hsl(233, 100%, 30%); }  .button--secondary {   background: hsl(200, 100%, 50%);   outline-color: hsl(200, 100%, 80%); }  .button--secondary:hover {   background: hsl(200, 100%, 40%); }  .button--secondary:active {   background: hsl(200, 100%, 30%); }

See the Pen
Basic buttons
by Jhey (@jh3y)
on CodePen.

That’s an awful lot of code for something not particularly complex. We haven’t added many styles and we’ve added a lot of rules to cater to the button’s different states and colors. We could significantly reduce the code with a scoped variable.

In our example, the only differing value between the two button variants is the hue. Let’s refactor that code a little then. We won’t change the markup but cleaning up the styles a little, we get this:

.button {   padding: 1rem 1.25rem;   color: #fff;   font-weight: bold;   font-size: 1.25rem;   margin: 1rem;   transition: background 0.1s ease;   background: hsl(var(--hue), 100%, 50%);   outline-color: hsl(var(--hue), 100%, 80%);  } .button:hover {   background: hsl(var(--hue), 100%, 40%); }  .button:active {   background: hsl(var(--hue), 100%, 30%); }  .button--primary {   --hue: 233; }  .button--secondary {   --hue: 200; }

See the Pen
Refactoring styles with a scoped variable
by Jhey (@jh3y)
on CodePen.

This not only reduces the code but makes maintenance so much easier. Change the core button styles in one place and it will update all the variants! 🙌

I’d likely leave it there to make it easier for devs wanting to use those buttons. But, we could take it further. We could inline the variable on the actual element and remove the class declarations completely. 😲

<button class="button" style="--hue: 233;">Primary</button> <button class="button" style="--hue: 200;">Secondary</button>

Now we don’t need these. 👍

.button--primary {   --hue: 233; }  .button--secondary {   --hue: 200; }

See the Pen
Scoping w/ inline CSS variables
by Jhey (@jh3y)
on CodePen.

Inlining those variables might not be best for your next design system or app but it does open up opportunities. Like, for example, if we had a button instance where we needed to override the color.

button.button.button--primary(style=`--hue: 20;`) Overridden

See the Pen
Overridden with inline scope
by Jhey (@jh3y)
on CodePen.

Having fun with inline variables

Another opportunity is to have a little fun with it. This is a technique I use for many of the Pens I create over on CodePen. 😉

You may be writing straightforward HTML, but in many cases, you may be using a framework, like React or a preprocessor like Pug, to write your markup. These solutions allow you to leverage JavaScript to create random inline variables. For the following examples, I’ll be using Pug. Pug is an indentation-based HTML templating engine. If you aren’t familiar with Pug, do not fear! I’ll try to keep the markup simple.

Let’s start by randomizing the hue for our buttons:

button.button(style=`--hue: $ {Math.random() * 360}`) First

With Pug, we can use ES6 template literals to inline randomized CSS variables. 💪

See the Pen
Random inline CSS variable hues
by Jhey (@jh3y)
on CodePen.

Animation alterations

So, now that we have the opportunity to define random characteristics for an element, what else could we do? Well, one overlooked opportunity is animation. True, we can’t animate the variable itself, like this:

@keyframes grow {   from { --scale: 1; }   to   { --scale: 2; } }

But we can create dynamic animations based on scoped variables. We can change the behavior of animation on the fly! 🤩

Example 1: The excited button

Let’s create a button that floats along minding its own business and then gets excited when we hover over it.

Start with the markup:

button.button(style=`--hue: $ {Math.random() * 360}`) Show me attention

A simple floating animation may look like this:

@keyframes flow {   0%, 100% {     transform: translate(0, 0);   }   50% {     transform: translate(0, -25%);   } }

This will give us something like this:

See the Pen
The excited button foundation
by Jhey (@jh3y)
on CodePen.

I’ve added a little shadow as an extra but it’s not vital. 👍

Let’s make it so that our button gets excited when we hover over it. Now, we could simply change the animation being used to something like this:

.button:hover {   animation: shake .1s infinite ease-in-out; }  @keyframes shake {   0%, 100% {     transform: translate(0, 0) rotate(0deg);   }   25% {     transform: translate(-1%, 3%) rotate(-2deg);   }   50% {     transform: translate(1%, 2%) rotate(2deg);   }   75% {     transform: translate(1%, -2%) rotate(-1deg);   } }

And it works:

See the Pen
The excited button gets another keyframes definition
by Jhey (@jh3y)
on CodePen.

But, we need to introduce another keyframes definition. What if we could merge the two animations into one? They aren’t too far off from each other in terms of structure.

We could try:

@keyframes flow-and-shake {   0%, 100% {     transform: translate(0, 0) rotate(0deg);   }   25%, 75% {     transform: translate(0, -12.5%) rotate(0deg);   }   50% {     transform: translate(0, -25%) rotate(0deg);   } }

Although this works, we end up with an animation that isn’t quite as smooth because of the translation steps. So what else could we do? Let’s find a compromise by removing the steps at 25% and 75%.

@keyframes flow-and-shake {   0%, 100% {     transform: translate(0, 0) rotate(0deg);   }   50% {     transform: translate(0, -25%) rotate(0deg);   } }

It works fine, as we expected, but here comes the trick: Let’s update our button with some variables.

.button {   --y: -25;   --x: 0;   --rotation: 0;   --speed: 2; }

Now let’s plug them into the animation definition, along with the button’s animation properties.

.button {   animation-name: flow-and-shake;   animation-duration: calc(var(--speed) * 1s);   animation-iteration-count: infinite;   animation-timing-function: ease-in-out; }  @keyframes flow-and-shake {   0%, 100% {     transform: translate(calc(var(--x) * -1%), calc(var(--y) * -1%))       rotate(calc(var(--rotation) * -1deg));   }   50% {     transform: translate(calc(var(--x) * 1%), calc(var(--y) * 1%))       rotate(calc(var(--rotation) * 1deg));   } }

All is well. 👍

Let’s change those values when the button is hovered:

.button:hover {   --speed: .1;   --x: 1;   --y: -1;   --rotation: -1; }

See the Pen
The excited button with refactored keyframes & scoped variables
by Jhey (@jh3y)
on CodePen.

Nice! Now our button has two different types of animations but defined via one set of keyframes. 🤯

Let’s have a little more fun with it. If we take it a little further, we can make the button a little more playful and maybe stop animating altogether when it’s active. 😅

See the Pen
The Excited Button w/ dynamic animation 🤓
by Jhey (@jh3y)
on CodePen.

Example 2: Bubbles

Now that we’ve gone through some different techniques for things we can do with the power of scope, let’s put it all together. We are going to create a randomly generated bubble scene that heavily leverages scoped CSS variables.

Let’s start by creating a bubble. A static bubble.

.bubble {   background: radial-gradient(100% 115% at 25% 25%, #fff, transparent 33%),     radial-gradient(15% 15% at 75% 75%, #80dfff, transparent),     radial-gradient(100% 100% at 50% 25%, transparent, #66d9ff 98%);   border: 1px solid #b3ecff;   border-radius: 100%;   height: 50px;   width: 50px; }

We are using background with multiple values and a border to make the bubble effect — but it’s not very dynamic. We know the border-radius will always be the same. And we know the structure of the border and background will not change. But the values used within those properties and the other property values could all be random.

Let’s refactor the CSS to make use of variables:

.bubble {   --size: 50;   --hue: 195;   --bubble-outline: hsl(var(--hue), 100%, 50%);   --bubble-spot: hsl(var(--hue), 100%, 75%);   --bubble-shade: hsl(var(--hue), 100%, 70%);   background: radial-gradient(100% 115% at 25% 25%, #fff, transparent 33%),     radial-gradient(15% 15% at 75% 75%, var(--bubble-spot), transparent),     radial-gradient(100% 100% at 50% 25%, transparent, var(--bubble-shade) 98%);   border: 1px solid var(--bubble-outline);   border-radius: 100%;   height: calc(var(--size) * 1px);   width: calc(var(--size) * 1px); }

That’s a good start. 👍

See the Pen
Bubbles foundation
by Jhey (@jh3y)
on CodePen.

Let’s add some more bubbles and leverage the inline scope to position them as well as size them. Since we are going to start randomizing more than one value, it’s handy to have a function to generate a random number in range for our markup.

- const randomInRange = (max, min) => Math.floor(Math.random() * (max - min + 1)) + min

With Pug, we can utilize iteration to create a large set of bubbles:

- const baseHue = randomInRange(0, 360) - const bubbleCount = 50 - let b = 0 while b < bubbleCount   - const size = randomInRange(10, 50)   - const x = randomInRange(0, 100)   .bubble(style=`--x: $ {x}; --size: $ {size}; --hue: $ {baseHue}`)   - b++

Updating our .bubble styling allows us to make use of the new inline variables.

.bubble {   left: calc(var(--x) * 1%);   position: absolute;   transform: translate(-50%, 0); }

Giving us a random set of bubbles:

See the Pen
Adding bubbles
by Jhey (@jh3y)
on CodePen.

Let’s take it even further and animate those bubbles so they float from top to bottom and fade out.

.bubble {   animation: float 5s infinite ease-in-out;   top: 100%; }  @keyframes float {   from {     opacity: 1;     transform: translate(0, 0) scale(0);   }   to {     opacity: 0;     transform: translate(0, -100vh) scale(1);   } }

See the Pen
Bubbles rising together
by Jhey (@jh3y)
on CodePen.

That’s pretty boring. They all do the same thing at the same time. So let’s randomize the speed, delay, end scale and distance each bubble is going to travel.

- const randomInRange = (max, min) => Math.floor(Math.random() * (max - min + 1)) + min - const baseHue = randomInRange(0, 360) - const bubbleCount = 50 - let b = 0 while b < bubbleCount   - const size = randomInRange(10, 50)   - const delay = randomInRange(1, 10)   - const speed = randomInRange(2, 20)   - const distance = randomInRange(25, 150)   - const scale = randomInRange(100, 150) / 100   - const x = randomInRange(0, 100)   .bubble(style=`--x: $ {x}; --size: $ {size}; --hue: $ {baseHue}; --distance: $ {distance}; --speed: $ {speed}; --delay: $ {delay}; --scale: $ {scale}`)   - b++

And now, let’s update our styles

.bubble {   animation-name: float;   animation-duration: calc(var(--speed) * 1s);   animation-delay: calc(var(--delay) * -1s);   animation-iteration-count: infinite;   animation-timing-function: ease-in-out; }  @keyframes float {   from {     opacity: 1;     transform: translate(-50%, 0) scale(0);   }   to {     opacity: 0;     transform: translate(-50%, calc(var(--distance) * -1vh)) scale(var(--scale));   } }

And we will get this:

See the Pen
Random bubble scene using variable scope 😎
by Jhey (@jh3y)
on CodePen.

With around 50 lines of code, you can create a randomly generated animated scene by honing the power of the scope! 💪

That’s it!

We can create some pretty cool things with very little code by putting CSS variables to use and leveraging some little tricks.

I do hope this article has raised some awareness for the power of CSS variable scope and I do hope you will hone the power and pass it on 😎

All the demos in this article are available in this CodePen collection.

The post The Power (and Fun) of Scope with CSS Custom Properties appeared first on CSS-Tricks.

CSS-Tricks

, , ,

Making a Chart? Try Using Mobx State Tree to Power the Data

Who loves charts? Everyone, right? There are lots of ways to create them, including a number of libraries. There’s D3.js, Chart.js, amCharts, Highcharts, and Chartist, to name only a few of many, many options.

But we don’t necessary need a chart library to create charts. Take Mobx-state-tree (MST), an intuitive alternative to Redux for managing state in React. We can build an interactive custom chart with simple SVG elements, using MST to manage and manipulate data for the chart. If you’ve attempted to build charts using something like D3.js in the past, I think you’ll find this approach more intuitive. Even if you’re an experienced D3.js developer, I still think you’ll be interested to see how powerful MST can be as a data architecture for visualizations.

Here’s an example of MST being used to power a chart:

This example uses D3’s scale functions but the chart itself is rendered simply using SVG elements within JSX. I don’t know of any chart library that has an option for flashing hamster points so this is a great example of why it’s great to build your own charts — and it’s not as hard as you might think!

I’ve been building charts with D3 for over 10 years and, while I love how powerful it is, I’ve always found that my code can end up being unwieldy and hard to maintain, especially when working with complex visualizations. MST has changed all that completely by providing an elegant way to separate the data handling from the rendering. My hope for this article is that it will encourage you to give it a spin.

Getting familiar with MST model

First of all, let’s cover a quick overview of what a MST model looks like. This isn’t an in-depth tutorial on all things MST. I only want to show the basics because, really, that’s all you need about 90% of the time.

Below is a Sandbox with the code for a simple to-do list built in MST. Take a quick look and then I’ve explain what each section does.

First of all, the shape of the object is defined with typed definitions of the attribute of the model. In plain English, this means an instance of the to-do model must have a title, which must be a string and will default to having a “done” attribute of false.

.model("Todo", {   title: types.string,   done: false //this is equivalent to types.boolean that defaults to false })

Next, we have the view and action functions. View functions are ways to access calculated values based on data within the model without making any changes to the data held by the model. You can think of them as read-only functions.

.views(self => ({   outstandingTodoCount() {     return self.todos.length - self.todos.filter(t => t.done).length;   } }))

Action functions, on the other hand, allow us to safely update the data. This is always done in the background in a non-mutable way.

.actions(self => ({   addTodo(title) {     self.todos.push({       id: Math.random(),       title     });   } }));

Finally, we create a new instance of the store:

const todoStore = TodoStore.create({   todos: [     {       title: "foo",       done: false     }   ] });

To show the store in action, I’ve added a couple of console logs to show the output of outStandingTodoCount() before and after triggering the toggle function of the first instance of a Todo.

console.log(todoStore.outstandingTodoCount()); // outputs: 1 todoStore.todos[0].toggle(); console.log(todoStore.outstandingTodoCount()); // outputs: 0

As you can see, MST gives us a data structure that allows us to easily access and manipulate data. More importantly, it’s structure is very intuitive and the code is easy to read at a glance — not a reducer in sight!

Let’s make a React chart component

OK, so now that we have a bit of background on what MST looks like, let’s use it to create a store that manages data for a chart. We’ll will start with the chart JSX, though, because it’s much easier to build the store once you know what data is needed.

Let’s look at the JSX which renders the chart.

The first thing to note is that we are using styled-components to organize our CSS. If that’s new to you, Cliff Hall has a great post that shows it in use with a React app.

First of all, we are rendering the dropdown that will change the chart axes. This is a fairly simple HTML dropdown wrapped in a styled component. The thing to note is that this is a controlled input, with the state set using the selectedAxes value from our model (we’ll look at this later).

<select   onChange={e =>     model.setSelectedAxes(parseInt(e.target.value, 10))   }   defaultValue={model.selectedAxes} >

Next, we have the chart itself. I’ve split up the axes and points in to their own components, which live in a separate file. This really helps keep the code maintainable by keeping each file nice and small. Additionally, it means we can reuse the axes if we want to, say, have a line chart instead of points. This really pays off when working on large projects with multiple types of chart. It also makes it easy to test the components in isolation, both programmatically and manually within a living style guide.

{model.ready ? (   <div>     <Axes       yTicks={model.getYAxis()}       xTicks={model.getXAxis()}       xLabel={xAxisLabels[model.selectedAxes]}       yLabel={yAxisLabels[model.selectedAxes]}     ></Axes>     <Points points={model.getPoints()}></Points>   </div> ) : (   <Loading></Loading> )}

Try commenting out the axes and points components in the Sandbox above to see how they work independently of each other.

Lastly, we’ll wrap the component with an observer function. This means that any changes in the model will trigger a re-render.

export default observer(HeartrateChart);

Let’s take a look at the Axes component:

As you can see, we have an XAxis and a YAxis. Each has a label and a set of tick marks. We go into how the marks are created later, but here you should note that each axis is made up of a set of ticks, generated by mapping over an array of objects with a label and either an x or y value, depending on which axis we are rendering.

Try changing some of the attribute values for the elements and see what happens… or breaks! For example, change the line element in the YAxis to the following:

<line x1={30} x2="95%" y1={0} y2={y} />

The best way to learn how to build visuals with SVG is simply to experiment and break things. 🙂

OK, that’s half of the chart. Now we’ll look at the Points component.

Each point on the chart is composed of two things: an SVG image and a circle element. The image is the animal icon and the circle provides the pulse animation that is visible when mousing over the icon.

Try commenting out the image element and then the circle element to see what happens.

This time the model has to provide an array of point objects which gives us four properties: x and y values used to position the point on the graph, a label for the point (the name of the animal) and pulse, which is the duration of the pulse animation for each animal icon. Hopefully this all seems intuitive and logical.

Again, try fiddling with attribute values to see what changes and breaks. You can try setting the y attribute of the image to 0. Trust me, this is a much less intimidating way to learn than reading the W3C specification for an SVG image element!

Hopefully this gives you an understanding and feel for how we are rendering the chart in React. Now, it’s just a case of creating a model with the appropriate actions to generate the points and ticks data we need to loop over in JSX.

Creating our store

Here is the complete code for the store:

I’ll break down the code into the three parts mentioned earlier:

  1. Defining the attributes of the model
  2. Defining the actions
  3. Defining the views

Defining the attributes of the model

Everything we define here is accessible externally as a property of the instance of the model and — if using an observable wrapped component — any changes to these properties will trigger a re-render.

.model('ChartModel', {   animals: types.array(AnimalModel),   paddingAndMargins: types.frozen({     paddingX: 30,     paddingRight: 0,     marginX: 30,     marginY: 30,     marginTop: 30,     chartHeight: 500   }),   ready: false, // means a types.boolean that defaults to false   selectedAxes: 0 // means a types.number that defaults to 0 })

Each animal has four data points: name (Creature), longevity (Longevity__Years_), weight (Mass__grams_), and resting heart rate (Resting_Heart_Rate__BPM_).

const AnimalModel = types.model('AnimalModel', {   Creature: types.string,   Longevity__Years_: types.number,   Mass__grams_: types.number,   Resting_Heart_Rate__BPM_: types.number });

Defining the actions

We only have two actions. The first (setSelectedAxes ) is called when changing the dropdown menu, which updates the selectedAxes attribute which, in turn, dictates what data gets used to render the axes.

setSelectedAxes(val) {   self.selectedAxes = val; },

The setUpScales action requires a bit more explanation. This function is called just after the chart component mounts, within a useEffect hook function, or after the window is resized. It accepts an object with the width of the DOM that contains the element. This allows us to set up the scale functions for each axis to fill the full available width. I will explain the scale functions shortly.

In order to set up scale functions, we need to calculate the maximum value for each data type, so the first thing we do is loop over the animals to calculate these maximum and minimum values. We can use zero as the minimum value for any scale we want to start at zero.

// ... self.animals.forEach(   ({     Creature,     Longevity__Years_,     Mass__grams_,     Resting_Heart_Rate__BPM_,     ...rest   }) => {     maxHeartrate = Math.max(       maxHeartrate,       parseInt(Resting_Heart_Rate__BPM_, 10)     );     maxLongevity = Math.max(       maxLongevity,       parseInt(Longevity__Years_, 10)     );     maxWeight = Math.max(maxWeight, parseInt(Mass__grams_, 10));     minWeight =       minWeight === 0         ? parseInt(Mass__grams_, 10)         : Math.min(minWeight, parseInt(Mass__grams_, 10));   } ); // ...

Now to set up the scale functions! Here, we’ll be using the scaleLinear and scaleLog functions from D3.js. When setting these up, we specify the domain, which is the minimum and maximum input the functions can expect, and the range, which is the maximum and minimum output.

For example, when I call self.heartScaleY with the maxHeartrate value, the output will be equal to marginTop. That makes sense because this will be at the very top of the chart. For the longevity attribute, we need to have two scale functions since this data will appear on either the x- or the y-axis, depending on which dropdown option is chosen.

self.heartScaleY = scaleLinear()   .domain([maxHeartrate, minHeartrate])   .range([marginTop, chartHeight - marginY - marginTop]); self.longevityScaleX = scaleLinear()   .domain([minLongevity, maxLongevity])   .range([paddingX + marginY, width - marginX - paddingX - paddingRight]); self.longevityScaleY = scaleLinear()   .domain([maxLongevity, minLongevity])   .range([marginTop, chartHeight - marginY - marginTop]); self.weightScaleX = scaleLog()   .base(2)   .domain([minWeight, maxWeight])   .range([paddingX + marginY, width - marginX - paddingX - paddingRight]);

Finally, we set self.ready to be true since the chart is ready to render.

Defining the views

We have two sets of functions for the views. The first set outputs the data needed to render the axis ticks (I said we’d get there!) and the second set outputs the data needed to render the points. We’ll take a look at the tick functions first.

There are only two tick functions that are called from the React app: getXAxis and getYAxis. These simply return the output of other view functions depending on the value of self.selectedAxes.

getXAxis() {   switch (self.selectedAxes) {     case 0:       return self.longevityXAxis;       break;     case 1:     case 2:       return self.weightXAxis;       break;   } }, getYAxis() {   switch (self.selectedAxes) {     case 0:     case 1:       return self.heartYAxis;       break;     case 2:       return self.longevityYAxis;       break;   } },

If we take a look at the Axis functions themselves we can see they use a ticks method of the scale function. This returns an array of numbers suitable for an axis. We then map over the values to return the data we need for our axis component.

heartYAxis() {   return self.heartScaleY.ticks(10).map(val => ({     label: val,     y: self.heartScaleY(val)   })); } // ...

Try changing the value of the parameter for the ticks function to 5 and see how it affects the chart: self.heartScaleY.ticks(5).

Now we have the view functions to return the data needed for the Points component.

If we take a look at longevityHeartratePoints (which returns the point data for the “Longevity vs. Heart” rate chart), we can see that we are looping over the array of animals and using the appropriate scale functions to get the x and y positions for the point. For the pulse attribute, we use some maths to convert the beats per minute value of the heart rate into a value representing the duration of a single heartbeat in milliseconds.

longevityHeartratePoints() {   return self.animals.map(     ({ Creature, Longevity__Years_, Resting_Heart_Rate__BPM_ }) => ({       y: self.heartScaleY(Resting_Heart_Rate__BPM_),       x: self.longevityScaleX(Longevity__Years_),       pulse: Math.round(1000 / (Resting_Heart_Rate__BPM_ / 60)),       label: Creature     })   ); },

At the end of the store.js file, we need to create a Store model and then instantiate it with the raw data for the animal objects. It is a common pattern to attach all models to a parent Store model which can then be accessed through a provider at top level if needed.

const Store = types.model('Store', {   chartModel: ChartModel }); const store = Store.create({   chartModel: { animals: data } }); export default store;

And that is it! Here’s our demo once again:


This is by no means the only way to organize data to build charts in JSX, but I have found it to be incredibly effective. I’ve have used this structure and stack in the wild to build a library of custom charts for a big corporate client and was blown away with how nicely MST worked for this purpose. I hope you have the same experience!

The post Making a Chart? Try Using Mobx State Tree to Power the Data appeared first on CSS-Tricks.

CSS-Tricks

, , , , , , ,
[Top]

How Web Content Can Affect Power Usage

Because we know that all people with battery-powered devices are constantly concerned about their battery levels, and that websites are significant consumers of that battery power, we should probably think about this stuff a lot more than we do.

I’d expect the browser itself to be our main ally here, doing smart things to reduce power consumption without us developers needing to think too much about it. But we’ve learned over the years that it’s always a shared responsibility. We regularly need to help the browser do its job the best it can (think responsive images and will-change).

Some direct tips from Benjamin Poulain and Simon Fraser’s article:

  • Minimize the use of timers to avoid waking up the CPU. Try to coalesce timer-based work into a few, infrequent timers. Lots of uncoordinated timers which trigger frequent CPU wake-ups are much worse than gathering that work into fewer chunks.
  • Minimize continually animating content, like animated images and auto-playing video. Be particularly vigilant to avoid “loading” spinner GIFs or CSS animations that continually trigger painting, even if you can’t see them. IntersectionObserver can be used to run animations only when they are visible.
  • Use declarative animations (CSS Animations and Transitions) where possible. The browser can optimize these away when the animating content is not visible, and they are more efficient than script-driven animation.
  • Avoid network polling to obtain periodic updates from a server. Use WebSockets or Fetch with a persistent connection, instead of polling.

I’d like to see more developer tooling along the lines of how macOS makes it easy to see apps that are demanding significant power:

WebKit DevTools does has it:

We used to have a Battery Status API, but that’s been deprecated, so not a big part of the story right now.

I was just at the Web Unleashed conferencewhere Kyle Simpson talked about this rather directly in his keynote lecture. His main idea is that we should ask users a bit more directly and solicit their preferences. Hey user, are you in a situation where you want to use as little battery power as possible? Tell us and we’ll do what we can to make that happen (even on a site-by-site basis).

Direct Link to ArticlePermalink

The post How Web Content Can Affect Power Usage appeared first on CSS-Tricks.

CSS-Tricks

, , ,
[Top]

The Power of Serverless v2.0! (Now an Open-Source Gatsby Site Hosted on Netlify)

I created a website called The Power of Serverless for Front-End Developers over at thepowerofserverless.info a little while back while I was learning about that whole idea. I know a little more now but still have an endless amount to learn. Still, I felt like it was time to revamp that site a bit.

For one thing, just like our little conferences website, the new site is a subdomain of this very site:

https://serverless.css-tricks.com/

Why? What’s this site all about?

The whole idea behind the serverless buzzword is a pretty big deal. Rather than maintaining your own servers, which you already buy from some other company, you architect your app such that everything is run on commoditized servers you access on-demand instead.

Hosting becomes static, which is rife with advantages. Just look at Netlify who offer blazing-fast static hosting and innovate around the developer experience. The bits you still need back-end services for run in cloud functions that are cheap and efficient.

This is a big deal for front-end developers. We’ve already seen a massive growth in what we are capable of doing on the front end, thanks to the expanding power of JavaScript. Now a JavaScript developer can be building entire websites from end-to-end with the JAMstack concept.

But you still need to know how to string it all together. Who do you use to process forms? Where do you store the data? What can I use for user authentication? What content management systems are available in this world? That’s what this site is all about! I’d like the site to be able to explain the concept and offer resources, but more importantly, be a directory to the slew of services out there that make up this new serverless world.

The site also features a section containing ideas that might help you figure out how you might use serverless technology. Perhaps you’ll even take a spin making a serverless site.

Design by Kylie Timpani and illustration by Geri Coady

Kylie Timpani (yes, the same Kylie who worked on the v17 design of this site!) did all the visual design for this project.

Geri Coady did all the illustration work.

If anything looks off or weird, blame my poor implementation of their work. I’m still making my way through checklists of improvements as we speak. Sometimes you just gotta launch things and improve as you go.

Everything is on GitHub and contributions are welcome

It’s all right here.

I’d appreciate any help cleaning up copy, adding services, making it more accessible… really anything you think would improve the site. Feel free to link up your own work, although I tend to find that contributions are stronger when you are propping up someone else rather than yourself. It’s ultimately my call whether your pull request is accepted. That might be subjective sometimes.

Before doing anything dramatic, probably best to talk it out by emailing me or opening an issue. There’s already a handful of issues in there.

I suspect companies that exist in this space will be interested in being represented in here somewhere, and I’m cool with that. Go for it. Perhaps we can open up some kind of sponsorship opportunities as well.

Creating with components: A good idea

I went with Gatsby for this project. A little site like this (a couple of pages of static content) deserves to be rendered entirely server-side. Gatsby does that, even though you work entirely in React, which is generally thought of as a client-side technology. Next.js and react-static are similar in spirit.

I purposely wanted to work in JavaScript because I feel like JavaScript has been doing the best job around the idea of architecting sites in components. Sure, you could sling some partials and pass local variables in Rails partials or Nunjucks includes, but it’s a far cry from the versatility you get in a framework like React, Vue or Angular that are designing entirely to help build components for the front end.

The fact that these JavaScript frameworks are getting first-class server-side rendering stories is big. Plus, after the site’s initial render, the site “hydrates” and you end up getting that SPA feel anyway… fantastic. Yet another thing that shows how a JavaScript-focused front-end developer is getting more and more powerful.

As an aside: I don’t have much experience with more complicated content data structures and JAMstack sites. I suspect once you’ve gone past this “little simple cards of data” structure, you might be beyond what front-matter Markdown files are best suited toward and need to get into a more full-fledged CMS situation, hopefully with a GraphQL endpoint to get whatever you need. Ripe space, for sure.

The post The Power of Serverless v2.0! (Now an Open-Source Gatsby Site Hosted on Netlify) appeared first on CSS-Tricks.

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]