Skip to content

Transitions & transforms

Edit on GitHub

Use the rotate-*, scale-*, and transition-* utilities to add motion to elements. They’re commonly combined: a transition-transform class is what makes a rotate-* or scale-* animate smoothly between states instead of snapping.

Transitions use the design system’s transition speed tokens. Durations automatically shorten when a user has prefers-reduced-motion enabled.

Rotate an element using a CSS rotate transform.

ClassRotation
rotate-00deg
rotate-9090deg
rotate-180180deg
-rotate-90-90deg
<img
src="arrow-16.svg"
alt=""
class="icon-sprite rotate-90"
width="16"
height="16"
/>

The most common use is rotating a chevron when an ancestor button or <details> is opened, using the button-expanded: or details-open: modifiers with rotate-180. Pair with transition-transform so the rotation animates.

<button type="button" aria-expanded="false">
<img
src="https://www.volvocars.com/static/shared/icons/v5/chevron-down-16.svg"
alt=""
class="icon-sprite transition-transform button-expanded:rotate-180"
width="16"
height="16"
/>
</button>
<details>
<summary>
<img
src="https://www.volvocars.com/static/shared/icons/v5/chevron-down-16.svg"
alt=""
class="icon-sprite transition-transform details-open:rotate-180"
width="16"
height="16"
/>
Show details
</summary>
</details>

In React, the same markup is produced by <Icon icon="chevron-down" /> from @volvo-cars/react-icons: apply the rotation and transition classes via its className prop.

Show details

The chevron rotates 180° when the details element is open.

Show codeHide code
<details class="bg-secondary p-16">
<summary class="flex items-center gap-8">
<img
class="icon-sprite transition-transform details-open:rotate-180"
width="16"
height="16"
src="https://www.volvocars.com/static/shared/icons/v5/chevron-down-16.svg"
alt=""
/>
Show details
</summary>
<p class="mt-16">
The chevron rotates 180° when the details element is open.
</p>
</details>

Scale an element using a CSS scale transform. The named values map to the design system’s interaction scales.

ClassScaleTypical use
scale-960.96Same as scale-pressed
scale-1001Reset to the default size
scale-1101.1Same as scale-zoomed
scale-pressed0.96Pressed/active feedback
scale-zoomed1.1Hover zoom for media and tap areas

A common pattern is zooming on hover and shrinking on press, either on the element itself (hover:, active:) or on a descendant of a hovered ancestor (button-hover:, link-hover:). See interaction states for how each modifier resolves.

<button
type="button"
class="transition-transform hover:scale-zoomed active:scale-pressed"
>
Press me
</button>
<a href="/cars" class="block overflow-hidden">
<img
src="car.avif"
alt=""
class="transition-transform link-hover:scale-zoomed"
/>
</a>

Without transition-transform the size change is instant. Pair scale utilities with a transition unless you want the snap.

Control which properties animate when an element’s state changes. Pick the narrowest transition that covers what you’re animating. Avoid transitioning everything.

ClassAnimates
transition-colorscolor, background-color, border-color, box-shadow, fill, stroke, text-decoration-color
transition-opacityopacity
transition-transformtransform, scale, rotate, translate, skew
transition-noneDisables transitions

All transitions use the default design system speed token, which automatically respects prefers-reduced-motion.

<button
type="button"
class="border-2 border-tertiary transition-colors hover:border-primary"
>
Hover me
</button>
Show codeHide code
<div class="flex gap-16">
<button
type="button"
class="border-2 border-tertiary p-16 transition-colors hover:border-primary"
>
With transition-colors
</button>
<button
type="button"
class="border-2 border-tertiary p-16 hover:border-primary"
>
Without transition
</button>
</div>
  • Use transition-transform with rotate-* or scale-* so state-driven rotation and scaling animate smoothly.
  • Use transition-colors for hover/selected/checked styles that change border-*, bg-*, or icon fill/stroke.
  • Use transition-opacity when toggling visibility via opacity, for example fading a tooltip or overlay in and out.

Use the fade-in utility class to fade an element in using the design system’s default animation curve, without writing custom keyframes.

<img
src="/images/decorative/ex60-road.avif"
alt=""
class="img fade-in"
width="1200"
height="675"
/>
Show codeHide code
<img
class="img fade-in"
src="/images/decorative/ex60-road.avif"
width="1200"
height="675"
alt=""
/>

fade-in animates opacity from 0 to 1 using the --v-transition-notable duration and easing, so it automatically respects prefers-reduced-motion.

The transition utilities above are built on three transition timing tokens (duration + easing). Use them directly in custom CSS when the utility classes don’t cover your needs.

Custom propertyValueUse case
--v-transition-default220ms easeThe default choice for most transitions
--v-transition-micro110ms easeFast, subtle feedback, such as press states
--v-transition-notable800ms easeSlower, noticeable motion, such as fading in/out

All three automatically shorten when a user has prefers-reduced-motion enabled.