---
title: "Motion"
description: "Scroll reveals, stagger, parallax, motion tokens and how reduced motion is handled."
requested_language: fr
language: en
translation_notice: "This page isn't translated yet"
url: https://docs.systhema.app/fr/concepts/motion
version: unreleased (main)
docs_index: https://docs.systhema.app/fr/llms.txt
---
> This page isn't translated yet. Showing English.


Systhema's motion is built from CSS classes plus three small listeners. Content fades in as it scrolls into view, siblings can cascade, media can move with parallax, and interactive elements ease between states. All of it respects `prefers-reduced-motion`. This page explains how the pieces fit; the class lists are on the linked styling pages.

## Scroll reveals

A reveal is two classes: an animation and a trigger.

- The animation is one of `animate-fadein`, `animate-fadeinup`, `animate-fadeindown`, `animate-fadeinleft`, `animate-fadeinright`, `animate-fadeinzoomout` or `animate-fadeout`. Each runs once for 1s on an ease-out-cubic curve and keeps its end state.
- The trigger is `aos` (or its long form `animates-on-scroll`). It keeps the element hidden, with no animation, until [`AosListener`](https://docs.systhema.app/fr/components/listeners.md#aoslistener) adds the `animated` class as the element's top edge comes 20px into the viewport.

Scroll this preview into view, or press **Replay**:

```tsx preview iframe height=420 title="The six reveal animations"
import { useState } from 'react'
import { Button } from '@systhemaui/next'

const animations = [
  'animate-fadein',
  'animate-fadeinup',
  'animate-fadeindown',
  'animate-fadeinleft',
  'animate-fadeinright',
  'animate-fadeinzoomout',
]

export default function Demo() {
  const [run, setRun] = useState(0)
  return (
    <div className="flex w-full flex-col items-center gap-8 overflow-hidden">
      <div key={run} className="grid w-full grid-cols-2 gap-4 md:grid-cols-3">
        {animations.map((animation) => (
          <div
            key={animation}
            className={`aos ${animation} bg-layout-alternative color-body text-small rounded-lg p-6 text-center`}
          >
            {animation}
          </div>
        ))}
      </div>
      <Button.button variant="secondary" onClick={() => setRun(run + 1)}>
        Replay
      </Button.button>
    </div>
  )
}
```

An element replays its reveal when it scrolls back below the fold and in again. Add `animates-once` to reveal it only the first time. Without JavaScript, or before the listener runs, `aos` elements stay hidden, which is why the listener ships in `SysthemaProvider` and in the [vanilla JS bundle](https://docs.systhema.app/fr/styling/vanilla-js.md) for HTML projects.

### Which components reveal

Most React components reveal on their own: `Heading`, `Paragraph`, `Card`, `Columns`, `Stack`, `Quote`, the media components and others add the classes in `packages.react.animationClasses` (`'aos animate-fadeinup'` in the templates). Pass `disableAnimation` to one component to turn it off, or leave `animationClasses` unset to turn reveals off project-wide. See [Configuration](https://docs.systhema.app/fr/concepts/configuration.md#packages).

### Containers reveal as one

A card, stack, hero, column, row, footer, accordion, quote, figure or media overlay that animates reveals as one composed unit: it cancels every reveal inside it, at any depth. So a `Card` fades in once with its heading and text, rather than in three steps. To let a `Stack`'s children reveal on their own, pass `allowChildAnimation` (the `aos-allow-children` class); for other containers, turn the container's own animation off. `aos-disable-children` cancels every reveal inside any element, and `animate-disable` cancels one. See [Animation](https://docs.systhema.app/fr/styling/animation.md#child-reveals).

## Stagger

`stagger-<n>` delays siblings so a row cascades: put it on each sibling, with `n` the number of items per row (1 to 10). The first item of each group of `n` starts at once and each next one waits one more step. A step is `--tw-stagger-delay` (300ms by default); `stagger-delay-<ms>` sets it, using the transition-duration scale.

```tsx preview iframe height=420 title="Three cards with stagger-3"
import { useState } from 'react'
import { Button, Card, Heading, Paragraph } from '@systhemaui/next'

const steps = ['Export', 'Sync', 'Build']

export default function Demo() {
  const [run, setRun] = useState(0)
  return (
    <div className="flex w-full flex-col items-center gap-8">
      <div key={run} className="stagger-delay-200 grid w-full gap-4 md:grid-cols-3">
        {steps.map((step) => (
          <Card key={step} className="stagger-3">
            <Heading.h4>{step}</Heading.h4>
            <Paragraph>Each card waits one step longer than the one before it.</Paragraph>
          </Card>
        ))}
      </div>
      <Button.button variant="secondary" onClick={() => setRun(run + 1)}>
        Replay
      </Button.button>
    </div>
  )
}
```

`stagger-delay-200` sits on the parent because `--tw-stagger-delay` is inherited. [`PostsList`](https://docs.systhema.app/fr/components/posts-list.md#reveal) staggers its grid columns with the same variable. The `animate-delay-*`, `animate-duration-*` and `animate-ease-*` utilities tune a single element; see [Animation timing](https://docs.systhema.app/fr/styling/animation-timing.md).

## Parallax

Media with the `parallax` class drifts against the scroll inside its `overflow: hidden` wrapper. [`ParallaxListener`](https://docs.systhema.app/fr/components/listeners.md#parallaxlistener) writes the transforms, the `Image` and `Video` components add the class with their `parallax` prop, and the `parallax` key of `systhema.config` sets the range and scale. See [Parallax](https://docs.systhema.app/fr/styling/parallax.md).

## Transitions and easing

Hover and focus changes use CSS transitions. `packages.react.transitionClasses` (`'duration-300 ease-out-cubic'` in the templates) is added to interactive components such as `Button`, `Link`, `Chip`, linked `Card`s and form controls. The easing curves exist as utilities (`ease-out-cubic`, `ease-in-out-quint`, …) and as `--ease-*` variables for your own CSS; see [Easing and transitions](https://docs.systhema.app/fr/styling/easing.md).

A designer can take over motion through two optional token collections: `easings` overrides the built-in curves one by one, and `transition` sets per-component durations and curves. Without them every component keeps its built-in behaviour. See [Motion tokens](https://docs.systhema.app/fr/design/motion-tokens.md).

## Reduced motion

When the visitor asks for reduced motion (`prefers-reduced-motion: reduce`):

- Every reveal shows its end state at once: fully visible, no transform. It never stays at `opacity: 0`, with or without JavaScript.
- The parallax engine writes no transform, and the media rests in its wrapper unmoved.
- Both listeners follow the preference live, so changing it mid-visit needs no reload.

Your own animations should do the same. Snap to the end state, not to hidden:

```css
@layer app {
  .marquee {
    animation: scroll 20s linear infinite;
  }

  @media (prefers-reduced-motion: reduce) {
    .marquee {
      animation: none;
    }
  }
}
```

## Related

- [Animation](https://docs.systhema.app/fr/styling/animation.md)
- [Animation timing](https://docs.systhema.app/fr/styling/animation-timing.md)
- [Listeners](https://docs.systhema.app/fr/components/listeners.md)
- [Motion tokens](https://docs.systhema.app/fr/design/motion-tokens.md)
- [Accessibility](https://docs.systhema.app/fr/concepts/accessibility.md)
