---
title: Animations
description: Built-in keyframe animations, duration and easing CSS variables,
  and parametric transform utilities.
package: moraine
version: 0.6.0
repository: https://github.com/subframe7536/moraine
---

# Animations

> Built-in keyframe animations, duration and easing CSS variables, and parametric transform utilities.

Moraine provides CSS animations through the UnoCSS preset and Tailwind 4 plugin. Built-in keyframes and transform utilities read the duration variables shown below. Set these variables in CSS; UnoCSS theme overrides cover design tokens, not animation controls.

## Built-in Animation Utilities

The following animation utilities are registered by `presetMoraine()` and `moraine/tailwind`:

| Utility                     | Description                                                         | Iteration |
| :-------------------------- | :------------------------------------------------------------------ | :-------- |
| `animate-mo-enter`          | Composable entrance animation with opacity, scale, and translation. | 1         |
| `animate-mo-exit`           | Composable exit animation for overlays and disclosures.             | 1         |
| `animate-accordion-down`    | Expand height from `0` to `var(--mo-collapsible-content-height)`.   | 1         |
| `animate-accordion-up`      | Collapse height from `var(--mo-collapsible-content-height)` to `0`. | 1         |
| `animate-spin`              | Infinite 360-degree rotation for progress rings and spinners.       | Infinite  |
| `animate-carousel`          | Continuous horizontal sliding translation (`-100%` to `100%`).      | Infinite  |
| `animate-shimmer`           | Linear horizontal sweep for Skeleton's gradient overlay.            | Infinite  |
| `animate-carousel-rtl`      | Reverse horizontal sliding translation (`100%` to `-100%`).         | Infinite  |
| `animate-carousel-vertical` | Continuous vertical sliding translation (`100%` to `-100%`).        | Infinite  |
| `animate-swing`             | Oscillating horizontal swing (`-60%` to `60%`).                     | Infinite  |
| `animate-swing-vertical`    | Oscillating vertical swing (`60%` to `-60%`).                       | Infinite  |
| `animate-elastic`           | Horizontal stretch and snap translation.                            | Infinite  |
| `animate-elastic-vertical`  | Vertical stretch and snap translation.                              | Infinite  |

## Parametric Transform Utilities

`animate-mo-enter` and `animate-mo-exit` read parametric CSS variables defined by matching utility classes. Combine them to create custom entrance and exit choreographies without writing custom keyframes:

```tsx
export function AnimatedPopover() {
  return (
    <div class="animate-mo-enter enter-opacity-0 enter-scale-95 enter-translate-y-2">
      Dropdown or popover content
    </div>
  )
}
```

| Utility pattern           | Target variable          | Values                        | Example                                       |
| :------------------------ | :----------------------- | :---------------------------- | :-------------------------------------------- |
| `enter-opacity-<val>`     | `--mo-enter-opacity`     | `0`–`100` percentage          | `enter-opacity-0`, `enter-opacity-50`         |
| `exit-opacity-<val>`      | `--mo-exit-opacity`      | `0`–`100` percentage          | `exit-opacity-0`                              |
| `enter-scale-<val>`       | `--mo-enter-scale`       | Scale percentage or arbitrary | `enter-scale-90`, `enter-scale-95`            |
| `exit-scale-<val>`        | `--mo-exit-scale`        | Scale percentage or arbitrary | `exit-scale-95`                               |
| `enter-translate-x-<val>` | `--mo-enter-translate-x` | Spacing or arbitrary          | `enter-translate-x-4`, `-enter-translate-x-2` |
| `enter-translate-y-<val>` | `--mo-enter-translate-y` | Spacing or arbitrary          | `enter-translate-y-2`, `-enter-translate-y-4` |
| `exit-translate-x-<val>`  | `--mo-exit-translate-x`  | Spacing or arbitrary          | `exit-translate-x-2`                          |
| `exit-translate-y-<val>`  | `--mo-exit-translate-y`  | Spacing or arbitrary          | `exit-translate-y-2`                          |
| `enter-rotate-<val>`      | `--mo-enter-rotate`      | Degrees or arbitrary          | `enter-rotate-12`, `-enter-rotate-45`         |
| `exit-rotate-<val>`       | `--mo-exit-rotate`       | Degrees or arbitrary          | `exit-rotate-12`                              |

## Animation Durations and Easing

Moraine defines default durations and easing curves for each animation type:

```css
:root {
  /* Global override for all Moraine CSS animations */
  --mo-anim-duration: 250ms;

  /* Type-specific overrides */
  --mo-anim-duration-enter: 250ms;
  --mo-anim-duration-exit: 150ms;
  --mo-anim-duration-loop: 2s;
  --mo-anim-duration-spin: 1s;

  /* Shared easing override for mo-enter and mo-exit */
  --mo-anim-ease: ease;
}
```

- **`--mo-anim-duration`** overrides every Moraine animation duration when defined.
- If `--mo-anim-duration` is not set, each animation falls back to its type-specific duration (`enter`, `exit`, `loop`, or `spin`).
- **`--mo-anim-ease`** overrides the easing of `animate-mo-enter` and `animate-mo-exit` in its scope. When unset, they read `--mo-anim-ease-enter` and `--mo-anim-ease-exit`, respectively, then fall back to the curves below. Set these variables on the animated element or a DOM ancestor; portaled content cannot inherit from a trigger's container outside the portal.
- **Easing curves**:
  - `enter`: `cubic-bezier(0.16, 1, 0.3, 1)` (snappy ease-out)
  - `exit`: `cubic-bezier(0.7, 0, 0.84, 0)` (accelerating ease-in)
  - `loop`: `ease-in-out`
  - `spin`: `linear`

`animate-shimmer` uses the loop duration with linear easing.

## Disclosure Height Token

Collapsible and Accordion components calculate their rendered panel dimensions and set `--mo-collapsible-content-height` on the disclosure container element. The `animate-accordion-down` and `animate-accordion-up` keyframes read this variable to animate smoothly between `0` and the exact natural content height.

```css
@keyframes accordion-down {
  from {
    height: 0;
    opacity: 0;
  }
  to {
    height: var(--mo-collapsible-content-height);
    opacity: 1;
  }
}
```

## Reduced Motion

To respect user preferences for reduced motion, pair animation utilities with `motion-reduce:animate-none` or `motion-reduce:transition-none`:

```tsx
<div class="animate-mo-enter enter-opacity-0 enter-scale-95 motion-reduce:animate-none">
  Dialog content
</div>
```

