
> This article is part of a series dedicated to **Web Animation**.

- [Web Animation](/en/blog/web-animation/)
- **CSS Transitions**
- [CSS Animations](/en/blog/css-animations/)
- _SVG Animations (Coming soon)_
- _JavaScript Animations (Coming soon)_
- _Canvas Animations (Coming soon)_

Here you'll find a detailed theoretical explanation of **CSS Transitions**. If you want to see implementation examples and use cases, check out the [video](https://youtu.be/iRAVt8gsIbM).

<div style="margin-bottom: 2em" />

#### CSS property state changes

Normally, when we change the value of one or more CSS properties, the result rendered by the browser updates instantly. We can see this in the following example, where the `width` and `box-shadow` values change immediately when hovering over the logo, or on tap if you're on a touch device.

<iframe
  height="300"
  style="width: 100%;"
  scrolling="no"
  title="WebAnimation: Hover without transition"
  src="https://codepen.io/nucliweb/embed/preview/ExjxdzL?height=300&theme-id=11883&default-tab=css,result"
  frameborder="no"
  allowtransparency="true"
  allowfullscreen="true"
>
  See the Pen{" "}
  <a href="https://codepen.io/nucliweb/pen/ExjxdzL">
    WebAnimation: Hover without transition
  </a>{" "}
  by Joan Leon (<a href="https://codepen.io/nucliweb">@nucliweb</a>) on{" "}
  <a href="https://codepen.io">CodePen</a>.
</iframe>
<div style="margin-bottom: 2em" />

Transitions are a presentation effect. The browser's rendering engine performs an [interpolation](https://en.wikipedia.org/wiki/Interpolation), calculating the intermediate values between one value and another.

Here we have the same example applying the transition.

<iframe
  height="300"
  style="width: 100%;"
  scrolling="no"
  title="WebAnimation: Hover with transition"
  src="https://codepen.io/nucliweb/embed/preview/OJVJBeV?height=300&theme-id=11883&default-tab=css,result"
  frameborder="no"
  allowtransparency="true"
  allowfullscreen="true"
>
  See the Pen{" "}
  <a href="https://codepen.io/nucliweb/pen/OJVJBeV">
    WebAnimation: Hover with transition
  </a>{" "}
  by Joan Leon (<a href="https://codepen.io/nucliweb">@nucliweb</a>) on{" "}
  <a href="https://codepen.io">CodePen</a>.
</iframe>
<div style="margin-bottom: 2em" />

#### How transitions work

If we have an element on our page that we want to move 200px to the right, we can do it with the `transform` property.

<div style="margin-bottom: 2em" />

> In the example we'll use a bit of JavaScript to add interaction, in this case we'll add a class to indicate the element's new state. _We could do this without JavaScript, with a checkbox and CSS selector combinations, but I believe JavaScript helps us add user interaction. But if there's interest, we'll see in a future CSS article how to do these things without JavaScript._

<div style="margin-bottom: 2em" />
<iframe
  height="300"
  style="width: 100%;"
  scrolling="no"
  title="CSSTransition: translate 200px"
  src="https://codepen.io/nucliweb/embed/preview/abOOKeL?height=300&theme-id=11883&default-tab=css,result"
  frameborder="no"
  allowtransparency="true"
  allowfullscreen="true"
>
  See the Pen{" "}
  <a href="https://codepen.io/nucliweb/pen/abOOKeL">
    CSSTransition: translate 200px
  </a>{" "}
  by Joan Leon (<a href="https://codepen.io/nucliweb">@nucliweb</a>) on{" "}
  <a href="https://codepen.io">CodePen</a>.
</iframe>
<div style="margin-bottom: 2em" />

As we've seen, the `transition` property tells the browser to perform interpolation. The browser will attempt, depending on available resources, to update page rendering at 60 frames per second.

If we consider that the displacement is 200px in 0.5 seconds, in an ideal case of 60 frames/second, the interpolation will be 30 frames. The browser will calculate for interpolation: `200px/30fps = 6.666px`, so it will move the element 6.666px to the right in each animation cycle.

In the following image we can see Chrome's Developer Tools representation of the transition.

![Developer Tools: Transition](https://res.cloudinary.com/nucliweb/image/upload/c_scale,dpr_auto,f_auto,q_auto,w_896/v1694434106/joanleon.dev/assets/transiciones-css/devtools-transition.png)

We have a graphical representation of the number of frames on the timeline, and we can also see the time it took to complete the transition **502.29 ms**.

<div style="margin-bottom: 2em" />

> In developer tools, both Chrome and Firefox provide a lot of information and tools. We'll analyze them in detail in a future article, and learn how we can use them to debug our animations or analyze animations on other websites (you learn a lot by analyzing other people's development).

<div style="margin-bottom: 2em" />

### CSS properties for transitions

Let's analyze in detail each of the CSS properties we have to define and control transitions.

#### transition-property

In this property we can define the CSS **property** we want to apply the transition to.

If we want to apply a transition between background color changes of an element, we should define it as follows: `transition-property: background-color`.

If we look at the **W3C** documentation we'll see the following table.

|                    |                                                     |
| ------------------ | --------------------------------------------------- |
| **Values**         | none OR [ all OR `<property-name>` ]#               |
| **Initial value**  | all                                                 |
| **Applies to**     | All elements and :before and :after pseudo-elements |
| **Computed value** | As specified                                        |
| **Inherited**      | No                                                  |
| **Animatable**     | No                                                  |

We'll see this table for almost all CSS properties in the official **W3C** documentation. It informs us of the possible values we can define for that property, the initial value it has if we don't define one, the elements it applies to, the computed value, whether it inherits the value from the ancestor element, and whether it's an animatable value.

A notable value from this information is the initial value, `all`, which means we can omit defining the property we want to animate. Another interesting piece of information is the hashtag `#` at the end of the values, indicating that the property accepts a comma-separated list of values.

So we could define the following to reference multiple properties.

```css
.logo {
  transition-property: width, box-shadow;
}
```

<div style="margin-bottom: 2em" />

> I'll dedicate an article to animation performance, and we'll analyze whether explicitly defining the properties we want to animate has an impact compared to leaving the default `all` value.

<div style="margin-bottom: 2em" />

#### transition-duration

With the `transition-duration` property we define the transition time, or transitions if we define a comma-separated list _(here we can also see the #)_. It accepts positive values with units of seconds (s) or milliseconds (ms).

|                    |                                                     |
| ------------------ | --------------------------------------------------- |
| **Values**         | `<time>`#                                           |
| **Initial value**  | 0s                                                  |
| **Applies to**     | All elements and :before and :after pseudo-elements |
| **Computed value** | As specified                                        |
| **Inherited**      | No                                                  |
| **Animatable**     | No                                                  |

<div style="margin-bottom: 2em" />

If we define a negative value, the browser ignores the rule and changes would apply without any transition.

![Negative values](https://res.cloudinary.com/nucliweb/image/upload/c_scale,dpr_auto,f_auto,q_auto,w_896/v1694434106/joanleon.dev/assets/transiciones-css/negative.png)

#### transition-timing-function

With the `transition-timing-function` property we have the option to define timing functions. These are a series of values: ease, linear, ease-in, ease-out, ease-in-out, step-start, step-end, steps(n, start) predefined from [Bézier Curves](https://en.wikipedia.org/wiki/B%C3%A9zier_curve) `cubic-bezier(x1, y1, x2, y2)`.

> Bézier Curves deserve a specific and detailed article, as we have a specification dedicated to their definition, [CSS Easing Functions Level 1](https://www.w3.org/TR/css-easing-1/), so we'll cover it in future articles analyzing the technical aspects and implementation use cases.

Here we have the table with basic information.

|                    |                                                     |
| ------------------ | --------------------------------------------------- |
| **Values**         | `<timing-function>`#                                |
| **Initial value**  | ease                                                |
| **Applies to**     | All elements and :before and :after pseudo-elements |
| **Computed value** | As specified                                        |
| **Inherited**      | No                                                  |
| **Animatable**     | No                                                  |

<div style="margin-bottom: 2em" />

We can see that the default value is `ease` which is equivalent to the Bézier Curve `cubic-bezier(0.25, 0.1, 0.25, 1)`.

![ease function](https://res.cloudinary.com/nucliweb/image/upload/c_scale,dpr_auto,f_auto,q_auto,w_896/v1694434106/joanleon.dev/assets/transiciones-css/ease.png)

This is its graphical representation. As I mentioned, we'll see all of them in more detail in a future article.

#### transition-delay

As its name indicates, the `transition-delay` property allows us to define a value to indicate the wait time before starting the transition.

The value table is identical to `transition-duration`.

|                    |                                                     |
| ------------------ | --------------------------------------------------- |
| **Values**         | `<time>`#                                           |
| **Initial value**  | 0s                                                  |
| **Applies to**     | All elements and :before and :after pseudo-elements |
| **Computed value** | As specified                                        |
| **Inherited**      | No                                                  |
| **Animatable**     | No                                                  |

<div style="margin-bottom: 2em" />

This property does allow defining a negative value. For example, if we have a value of `-250ms` in a transition with a duration of `500ms`, the animation will have half the journey. And if the negative value is greater than its positive `transition-duration` equivalent, we won't get a transition, but an immediate change between the two states.

Here we can see an example of defining a negative value in the `transition-delay` property.

<iframe
  height="300"
  style="width: 100%;"
  scrolling="no"
  title="CSSTransition: negative delay"
  src="https://codepen.io/nucliweb/embed/preview/jOPrvMP?height=300&theme-id=11883&default-tab=css,result"
  frameborder="no"
  allowtransparency="true"
  allowfullscreen="true"
>
  See the Pen{" "}
  <a href="https://codepen.io/nucliweb/pen/jOPrvMP">
    CSSTransition: negative delay
  </a>{" "}
  by Joan Leon (<a href="https://codepen.io/nucliweb">@nucliweb</a>) on{" "}
  <a href="https://codepen.io">CodePen</a>.
</iframe>
<div style="margin-bottom: 2em" />

#### transition (shorthand)

Many CSS properties have a _shorthand_ (abbreviated form). It allows us to use one property with multiple values. Let's see an example that I think is much easier to understand.

```css
/* Transition with multiple properties */
.logo {
  transition-property: width;
  transition-duration: 0.5s;
  transition-timing-function: ease-out;
  transition-delay: 0.25s;
}

/* Transition with shorthand */
.logoJL {
  transition: width 0.5s ease-out 0.25s;
}
```

<div style="margin-bottom: 2em" />

In the code above, both classes would apply the same transition. When we define a transition as shorthand in our code, if we inspect it with DevTools we can see it like this.

![DevTools: Shorthand transition](https://res.cloudinary.com/nucliweb/image/upload/c_scale,dpr_auto,f_auto,q_auto,w_896/v1694434106/joanleon.dev/assets/transiciones-css/devtools-transition-shothand.png)

We can see there's a triangle, indicating there's more information available in that CSS property. If we click on it, we can observe that internally the browser interprets them separately.

![DevTools: Expanded shorthand transition](https://res.cloudinary.com/nucliweb/image/upload/c_scale,dpr_auto,f_auto,q_auto,w_896/v1694434106/joanleon.dev/assets/transiciones-css/devtools-transition-shothand-open.png)

As with previous properties, in `transition` we also have the informative table from the specification.

|                    |                                                     |
| ------------------ | --------------------------------------------------- |
| **Values**         | `<single-transition>`#                              |
| **Initial value**  | all 0s ease 0s                                      |
| **Applies to**     | All elements and :before and :after pseudo-elements |
| **Computed value** | As specified                                        |
| **Inherited**      | No                                                  |
| **Animatable**     | No                                                  |

**&lt;single-transition&gt;** = [ [ none &Vert; &lt;transition-property&gt; ] &Vert; &lt;time&gt; &Vert; &lt;transition-timingfunction&gt; &Vert; &lt;time&gt; ]#

In the examples we've been seeing, I've been using the transition as shorthand, as it's the most common approach.

## Working with transitions

<div style="margin-bottom: 2em" />

#### Default values

When working with transitions, we need to understand how the browser interprets the syntax.

The default value of `transition-property` or as a value in the shorthand `transition` is `all`, which means the transition we apply will affect all properties (that allow it) of the CSS class where we're indicating it.

```css
.logo {
  transition-property: all;
  transition-duration: 250ms;
  transition-timing-function: ease;
}

.logoJL {
  transition: all 250ms ease;
}
```

In the code above, both classes do the same thing, but there's a detail we should consider. If we look at what the default values are for each of these properties, we can see we're defining values that the browser already has by default.

```css
.logo {
  transition-duration: 250ms;
}

.logoJL {
  transition: 250ms;
}
```

This code will have exactly the same behavior, even though we haven't defined values for `transition-property` and `transition-timing-function`.

> For semantic reasons and later code interpretation, whether by ourselves in the future or by another person, it's common to (at least) find the `transition-property` value defined.

#### Multiple transitions

We've already seen this from the first example in the article.

```css
.logo {
  ... transition: width 0.25s, box-shadow 0.25s;
  ...;
}
```

We've defined transitions independently for `width` and `box-shadow`. In this case we could have done it with a value of `all .25s`, it would have the same effect. But doing it separately allows us to define different times, delays, or timing functions for each property.

```css
.logo {
  ... transition-property: width, box-shadow;
  transition-duration: 0.25s, 250ms;
  transition-timing-function: ease, ease-in-out;
  ...;
}

.logoJL {
  ... transition: width 0.25s, box-shadow 0.25s ease-in-out;
  ...;
}
```

> As an example I've used units in seconds and milliseconds, to show they can coexist without problems.

In case we define multiple properties, where the number of properties is greater than the number of values in the duration property, it repeats.

In the following code, the values of `transition-duration` and `transition-timing-function` will repeat until they match the number of values in `transition-property`.

```css
.logo {
  ... transition-property: width, box-shadow, color, padding, border-radius;
  transition-duration: 0.25s, 250ms;
  transition-timing-function: ease, ease-in-out;
  ...;
}
```

Resulting in:

```css
.logo {
  ... transition-property: width, box-shadow, color, padding, border-radius;
  transition-duration: 0.25s, 250ms, 0.25s, 250ms, 0.25s;
  transition-timing-function: ease, ease-in-out, ease, ease-in-out, ease;
  ...;
}
```

![CSS Transitions Canonical Order](https://res.cloudinary.com/nucliweb/image/upload/c_scale,dpr_auto,f_auto,q_auto,w_896/v1694434106/joanleon.dev/assets/transiciones-css/css-transitions-canonical-order.png)

#### Value order

As the _W3C_ documentation itself indicates, the order of values in the `transition` property is important. As we've seen in the tables above, it tells us `<single-transition> = [ none | <single-transition-property> ] || <time> || <timing-function> || <time>`. This means the browser expects: **none** or a **property**, followed by duration, timing function, and finally delay time.

I've been testing various combinations, the browser is intelligent enough to correct errors in order.

```css
.logo {
  transition: ease-in opacity 250ms 3s;
}
```

In the code above, the browser will be able to interpret the transition even though the values aren't ordered following the standard. Even if the first time value is negative, it interprets we're indicating the `transition.delay` value since we can't have a negative value in `transition-duration`. I invite you to try combinations to understand how it works.

## How do transitions work internally?

Beyond knowing how to define our transitions to add animations and interactions, we can look at browser code to see how they're applying what we can see in the _W3C_ specifications, which in fact is documentation for browser developers, let's be clear.

We're fortunate to have the source code of most browsers available.

- [Chromium](https://github.com/chromium/chromium) the engine used in Google Chrome, Opera and recently by Microsoft's browser
- [Gecko](https://github.com/mozilla/gecko-dev) the engine used by Mozilla for Firefox
- [WebKit](https://github.com/WebKit/webkit) was Chrome and Opera's engine, but now only Safari uses it

It's interesting to see code like the [following](https://github.com/chromium/chromium/blob/master/third_party/blink/renderer/core/css/properties/shorthands/shorthands_custom.cc#L2819-L2857), where we can see exactly how it creates the list of values for a shorthand `transition`.

```c
const CSSValue* Transition::CSSValueFromComputedStyleInternal(
    const ComputedStyle& style,
    const SVGComputedStyle&,
    const LayoutObject*,
    bool allow_visited_style) const {
  const CSSTransitionData* transition_data = style.Transitions();
  if (transition_data) {
    CSSValueList* transitions_list = CSSValueList::CreateCommaSeparated();
    for (wtf_size_t i = 0; i < transition_data->PropertyList().size(); ++i) {
      CSSValueList* list = CSSValueList::CreateSpaceSeparated();
      list->Append(*ComputedStyleUtils::CreateTransitionPropertyValue(
          transition_data->PropertyList()[i]));
      list->Append(*CSSNumericLiteralValue::Create(
          CSSTimingData::GetRepeated(transition_data->DurationList(), i),
          CSSPrimitiveValue::UnitType::kSeconds));
      list->Append(*ComputedStyleUtils::CreateTimingFunctionValue(
          CSSTimingData::GetRepeated(transition_data->TimingFunctionList(), i)
              .get()));
      list->Append(*CSSNumericLiteralValue::Create(
          CSSTimingData::GetRepeated(transition_data->DelayList(), i),
          CSSPrimitiveValue::UnitType::kSeconds));
      transitions_list->Append(*list);
    }
    return transitions_list;
  }

  CSSValueList* list = CSSValueList::CreateSpaceSeparated();
  // transition-property default value.
  list->Append(*CSSIdentifierValue::Create(CSSValueID::kAll));
  list->Append(
      *CSSNumericLiteralValue::Create(CSSTransitionData::InitialDuration(),
                                      CSSPrimitiveValue::UnitType::kSeconds));
  list->Append(*ComputedStyleUtils::CreateTimingFunctionValue(
      CSSTransitionData::InitialTimingFunction().get()));
  list->Append(
      *CSSNumericLiteralValue::Create(CSSTransitionData::InitialDelay(),
                                      CSSPrimitiveValue::UnitType::kSeconds));
  return list;
}
```

<div style="margin-bottom: 2em" />

## Conclusions

Transitions were the first step to having animations on the web, they're as basic as they are powerful. They allow us to define highly optimized animations. By combining multiple properties, duration times, and delay times, we can create small animations. But when we need to create animations with more than two states, we need to use the `animation` properties along with `@keyframes`, but we'll see that in the next article in this series.
