---
title: "Easing and transitions"
description: "Easing curves, --ease-*, --duration-* and --transition-* variables."
requested_language: nl
language: en
translation_notice: "This page isn't translated yet"
url: https://docs.systhema.app/nl/next/styling/easing
version: unreleased (main)
docs_index: https://docs.systhema.app/nl/next/llms.txt
---
> This page isn't translated yet. Showing English.


Systhema ships the [easings.net](https://easings.net/) curves for motion that feels deliberate: sine, quad, cubic, quart, quint, expo, circ and back, each as `in`, `out` and `in-out`. Every curve exists in three forms generated from one table, so they never disagree: an `ease-*` utility for transitions, an `animate-ease-*` utility for [animations](https://docs.systhema.app/nl/next/styling/animation-timing.md), and an `--ease-*` custom property for your own CSS.

## Quick reference

<!-- generated:utilities easing-variables -->

| Variable              | Value                                 | Class               |
| --------------------- | ------------------------------------- | ------------------- |
| `--ease-in-sine`      | `cubic-bezier(0.12, 0, 0.39, 0)`      | `ease-in-sine`      |
| `--ease-out-sine`     | `cubic-bezier(0.61, 1, 0.88, 1)`      | `ease-out-sine`     |
| `--ease-in-out-sine`  | `cubic-bezier(0.37, 0, 0.63, 1)`      | `ease-in-out-sine`  |
| `--ease-in-quad`      | `cubic-bezier(0.11, 0, 0.5, 0)`       | `ease-in-quad`      |
| `--ease-out-quad`     | `cubic-bezier(0.5, 1, 0.89, 1)`       | `ease-out-quad`     |
| `--ease-in-out-quad`  | `cubic-bezier(0.45, 0, 0.55, 1)`      | `ease-in-out-quad`  |
| `--ease-in-cubic`     | `cubic-bezier(0.32, 0, 0.67, 0)`      | `ease-in-cubic`     |
| `--ease-out-cubic`    | `cubic-bezier(0.33, 1, 0.68, 1)`      | `ease-out-cubic`    |
| `--ease-in-out-cubic` | `cubic-bezier(0.65, 0, 0.35, 1)`      | `ease-in-out-cubic` |
| `--ease-in-quart`     | `cubic-bezier(0.5, 0, 0.75, 0)`       | `ease-in-quart`     |
| `--ease-out-quart`    | `cubic-bezier(0.25, 1, 0.5, 1)`       | `ease-out-quart`    |
| `--ease-in-out-quart` | `cubic-bezier(0.76, 0, 0.24, 1)`      | `ease-in-out-quart` |
| `--ease-in-quint`     | `cubic-bezier(0.64, 0, 0.78, 0)`      | `ease-in-quint`     |
| `--ease-out-quint`    | `cubic-bezier(0.22, 1, 0.36, 1)`      | `ease-out-quint`    |
| `--ease-in-out-quint` | `cubic-bezier(0.83, 0, 0.17, 1)`      | `ease-in-out-quint` |
| `--ease-in-expo`      | `cubic-bezier(0.7, 0, 0.84, 0)`       | `ease-in-expo`      |
| `--ease-out-expo`     | `cubic-bezier(0.16, 1, 0.3, 1)`       | `ease-out-expo`     |
| `--ease-in-out-expo`  | `cubic-bezier(0.87, 0, 0.13, 1)`      | `ease-in-out-expo`  |
| `--ease-in-circ`      | `cubic-bezier(0.55, 0, 1, 0.45)`      | `ease-in-circ`      |
| `--ease-out-circ`     | `cubic-bezier(0, 0.55, 0.45, 1)`      | `ease-out-circ`     |
| `--ease-in-out-circ`  | `cubic-bezier(0.85, 0, 0.15, 1)`      | `ease-in-out-circ`  |
| `--ease-in-back`      | `cubic-bezier(0.36, 0, 0.66, -0.56)`  | `ease-in-back`      |
| `--ease-out-back`     | `cubic-bezier(0.34, 1.56, 0.64, 1)`   | `ease-out-back`     |
| `--ease-in-out-back`  | `cubic-bezier(0.68, -0.6, 0.32, 1.6)` | `ease-in-out-back`  |

<!-- /generated -->

Elastic and bounce curves can't be written as a single `cubic-bezier()`, so they are not part of the set.

## Basic usage

Each dot below crosses its track with one curve over the same second. Press **Play** to send them back the other way. `in` curves start slowly, `out` curves land softly, `in-out` do both, and `back` overshoots.

```tsx preview iframe height=900 title="Easing curves"
import { useEffect, useState } from 'react'
import { Button } from '@systhemaui/next'

const curves: [string, number, number, number, number][] = [
  ['ease-in-sine', 0.12, 0, 0.39, 0],
  ['ease-out-sine', 0.61, 1, 0.88, 1],
  ['ease-in-out-sine', 0.37, 0, 0.63, 1],
  ['ease-in-quad', 0.11, 0, 0.5, 0],
  ['ease-out-quad', 0.5, 1, 0.89, 1],
  ['ease-in-out-quad', 0.45, 0, 0.55, 1],
  ['ease-in-cubic', 0.32, 0, 0.67, 0],
  ['ease-out-cubic', 0.33, 1, 0.68, 1],
  ['ease-in-out-cubic', 0.65, 0, 0.35, 1],
  ['ease-in-quart', 0.5, 0, 0.75, 0],
  ['ease-out-quart', 0.25, 1, 0.5, 1],
  ['ease-in-out-quart', 0.76, 0, 0.24, 1],
  ['ease-in-quint', 0.64, 0, 0.78, 0],
  ['ease-out-quint', 0.22, 1, 0.36, 1],
  ['ease-in-out-quint', 0.83, 0, 0.17, 1],
  ['ease-in-expo', 0.7, 0, 0.84, 0],
  ['ease-out-expo', 0.16, 1, 0.3, 1],
  ['ease-in-out-expo', 0.87, 0, 0.13, 1],
  ['ease-in-circ', 0.55, 0, 1, 0.45],
  ['ease-out-circ', 0, 0.55, 0.45, 1],
  ['ease-in-out-circ', 0.85, 0, 0.15, 1],
  ['ease-in-back', 0.36, 0, 0.66, -0.56],
  ['ease-out-back', 0.34, 1.56, 0.64, 1],
  ['ease-in-out-back', 0.68, -0.6, 0.32, 1.6],
]

export default function Demo() {
  const [end, setEnd] = useState(false)

  useEffect(() => {
    const timer = setTimeout(() => setEnd(true), 400)
    return () => clearTimeout(timer)
  }, [])

  return (
    <div className="flex w-full flex-col items-center gap-6">
      <div className="grid w-full grid-cols-1 gap-x-8 gap-y-4 md:grid-cols-3">
        {curves.map(([name, x1, y1, x2, y2]) => (
          <div key={name} className="flex items-center gap-3">
            <svg
              aria-hidden="true"
              focusable="false"
              viewBox="0 0 1 1"
              className="size-10 shrink-0 overflow-visible"
            >
              <rect
                width="1"
                height="1"
                fill="none"
                stroke="var(--color-foundations-line-muted)"
                strokeWidth="0.03"
              />
              <path
                d={`M0 1 C${x1} ${1 - y1} ${x2} ${1 - y2} 1 0`}
                fill="none"
                stroke="var(--color-foundations-primary-bg)"
                strokeWidth="0.06"
              />
            </svg>
            <div className="flex min-w-0 grow flex-col gap-1">
              <code className="text-small color-label">{name}</code>
              <div className="relative mx-2 h-3 rounded-full bg-(--color-foundations-line-muted)">
                <span
                  className={`absolute top-0 size-3 -translate-x-1/2 rounded-full bg-(--color-foundations-primary-bg) transition-[left] duration-1000 motion-reduce:transition-none ${name} ${end ? 'left-full' : 'left-0'}`}
                />
              </div>
            </div>
          </div>
        ))}
      </div>
      <Button.button variant="secondary" onClick={() => setEnd(!end)}>
        Play
      </Button.button>
    </div>
  )
}
```

For a transition, pair an `ease-*` class with Tailwind's `transition-*` and `duration-*` utilities. Hover the cards to compare a standard curve with an overshooting one:

```tsx preview iframe height=280 title="Transition easing"
export default function Demo() {
  return (
    <div className="grid w-full grid-cols-2 gap-4">
      <div className="rounded-lg border border-(--color-foundations-surface-border) bg-(--color-foundations-surface-bg) p-6 transition-transform duration-300 ease-out-cubic hover:-translate-y-2">
        <p className="text-h5 color-heading">ease-out-cubic</p>
        <p className="text-small color-body">Hover to lift</p>
      </div>
      <div className="rounded-lg border border-(--color-foundations-surface-border) bg-(--color-foundations-surface-bg) p-6 transition-transform duration-300 ease-out-back hover:-translate-y-2">
        <p className="text-h5 color-heading">ease-out-back</p>
        <p className="text-small color-body">Hover to lift</p>
      </div>
    </div>
  )
}
```

```html
<a class="transition-colors duration-300 ease-out-cubic hover:text-base-600" href="/pricing">Pricing</a>
```

### In your own CSS

The `--ease-*` variables are declared on `:root` in the base layer, so they exist whether or not the matching utility appears in your markup. Use them when hand-written CSS has to match an easing a Systhema component uses, instead of copying the literal:

```css title="src/app/(site)/globals.css"
@layer app {
  .drawer {
    transition: transform 300ms var(--ease-out-cubic);
  }
}
```

## Reduced motion

CSS transitions are not switched off for you. Add Tailwind's `motion-reduce:transition-none` (or `motion-safe:` on the transition classes) to anything that moves, as the curve demo above does. Systhema's [fade animations](https://docs.systhema.app/nl/next/styling/animation.md#reduced-motion) handle reduced motion themselves.

## Responsive and state variants

`ease-*` takes every variant: `hover:ease-out-back` applies a different curve while hovered, `md:ease-in-out-quint` from `md` up.

## Customizing

A project's `easings` token collection overrides the built-in curves one by one: a Figma curve named `easeOutCubic` replaces `ease-out-cubic`, `animate-ease-out-cubic` and `--ease-out-cubic` together, and any curve the collection doesn't declare keeps its built-in value. The same collection can declare durations, emitted as `--duration-<name>`, and a `transition` collection adds per-component `--transition-*` variables (the button reads them today). The default tokens ship curves only, so no `--duration-*` or `--transition-*` variable exists until your tokens declare one.

- [Motion tokens](https://docs.systhema.app/nl/next/design/motion-tokens.md) explains both collections.
- The [motion reference](https://docs.systhema.app/nl/next/reference/tokens/motion.md) lists every curve with its token name.

## Related

- [Animation timing](https://docs.systhema.app/nl/next/styling/animation-timing.md)
- [Motion](https://docs.systhema.app/nl/next/concepts/motion.md)
- [Motion tokens](https://docs.systhema.app/nl/next/design/motion-tokens.md)
