> ## Oymyakon DS index
> Fetch the full index at: https://super-dollop-pzmo65r.pages.github.io/llms.txt
> HTML preview: https://super-dollop-pzmo65r.pages.github.io/motion.html
> Source: tokens/rules/motion-rules.md, tokens/generated/motion.css

---

# Animation Rules (Oymyakon DS · Motion 1.7.0)

Source: Figma — `🕹️ Oymyakon 3.20.0 (components)`.
All motion tokens live in the **Variables panel** (collection `Motion`).

**In code the tokens come from `tokens/generated/motion.css`**, built from the DTCG sources in
`tokens/src/motion/` (`primitives.json`, `presets.json`, `loops.json`) by
`node tokens/scripts/build-dtcg.js` and gated by `validate-dtcg.js`. §8 below is the reference
listing; the generated file is what a component consumes. A component never declares a motion
token of its own and never needs its own reduced-motion override — both happen at the token.

| Category | Purpose |
|---|---|
| `Curves` | Easing curve primitives — define the character of movement |
| `Duration` | Duration primitives — set the overall length of an animation |
| `Transforming` | Preset tokens — a combination of Curves + Duration, time-based behavior patterns |
| `Patterns` | Ready-to-use reusable animation solutions defined in the design system |

Read these rules **before every task** that involves motion: when building interactive components, when describing transitions in a specification, when reviewing layouts.

---

## 0. Core Principles

Every movement, transition, or effect in an interface must have a clear purpose and meaning that is understandable to the user in the current context. Animation should not be merely decorative; it must:

1. **Inform:** explain to the user what is happening, why, and where.
2. **Guide:** draw attention to important elements or next steps.
3. **Create a sense of connection:** help the user understand the relationships between interface elements.
4. **Provide feedback:** animation can visually confirm that an action was completed.

Without context, animation can become distracting, disorienting, or even irritating — creating a feeling of "magic" without logic.

### 0.1 Naturalness

Objects should move realistically, simulating weight, inertia, and obedience to physical forces (for example, decelerating to a stop or bouncing on collision). This makes the interface feel more responsive and predictable for the user.

### 0.2 Consistency

Animations should harmonize with the overall style of the interface, providing a uniform and recognizable user experience. They are meant to create a sense of continuity and smooth transitions, preventing user disorientation.

### 0.3 Moderation

Avoid excessive or prolonged animations, as they can distract the user from the core functionality. Animations should complement the interface harmoniously, not compete with it. It is critically important to give users the ability to disable them in accessibility settings, especially for people with motion sensitivity.

---

## 0.4 Recommendations: when to use animation

### Situations where we use animation

- **Feedback for user actions:** animation can confirm an action. For example, changing a button's color after it is clicked.
- **Visual cues:** animated transitions can help the user understand how the application works. For example, a swipe animation for deleting a list item.
- **Loading and waiting:** animated loading indicators can "bring a screen to life."
- **Empty states and errors:** animations can soften frustration.

### Situations where we do not use animation

- **Interface overload** — using too much animation can be distracting and annoying.
- **Complex and lengthy animations** — they can slow down the perception of the interface.
- **Inappropriate contexts** — it is better not to use animation if it adds no value or hinders understanding.
- **Do not disable the system overscroll/overdrag animation.** It forms the expected behavior during scrolling. Blocking or replacing this animation can create a feeling of a "broken" interface.

---

## 1. Tokens and Presets

Animation tokens are standardized values that define the behavior of animations in an interface, ensuring uniformity. We distinguish four main types of tokens:

1. **Curves:** primitive tokens. Define how objects will accelerate and decelerate throughout an animation. They allow movement to feel more natural and smooth.
2. **Duration:** primitive tokens. Set the overall duration of an animation, affecting its execution speed.
3. **Transforming:** preset tokens created by combining Curves and Duration. Time-based animation behavior patterns. They control how objects appear, move, or disappear.
4. **Patterns:** preset tokens. Ready-made collections that represent standardized, reusable animation solutions defined in the design system.

### 1.0 Role of Primitives

- **Primitives (Curves, Duration)** are used to create animation presets.
- Primitives are available to other teams so they can create their own presets based on the design system's standard values.
- Primitives are **not applied directly** in components — only through `Transforming/*` or `Patterns/*` presets.

### 1.1 Where we use tokens

Animation tokens are intended for showing and hiding components within screens. However, for transitions between screens, native platform animations should be used to ensure consistency with the operating system.

### 1.2 How to create new presets

New presets can be created by any team from the provided primitives and animation tokens from the design system.

### 1.3 Development

In development, animation patterns are represented as modifiers that teams can reuse.

Modifier parameters can be changed to adapt the animation to specific needs. It is recommended to use the default values.

If a custom animation or modification of an existing pattern is required, contact the design system team.

---

## 2. Curves Primitives (easing curves)

**Curves (Easing Curves):** define how objects will accelerate and decelerate throughout an animation. They allow movement to feel more natural, smooth, and aesthetically pleasing.

We use **four curve tokens:**

### 2.1 `accelerated-ease-in`

| Parameter | Value |
|---|---|
| **cubic-bezier** | `0.5, 0, 0.75, 0` |
| **X1** | 0.5 |
| **Y1** | 0 |
| **X2** | 0.75 |
| **Y2** | 0 |

The animation starts slowly and gradually accelerates toward the end. It suits elements that are leaving the screen, creating a sense of rapid disappearance.

**Example use case:** closing a modal window (moving up or down).

### 2.2 `standard-ease-in-out`

| Parameter | Value |
|---|---|
| **cubic-bezier** | `0.25, 1, 0.5, 1` |
| **X1** | 0.25 |
| **Y1** | 1 |
| **X2** | 0.5 |
| **Y2** | 1 |

The animation starts quickly and gradually decelerates toward the end. It suits elements that appear on screen from outside, drawing attention to their final position.

**Example use case:** a new window appearing (moving from bottom to top).

### 2.3 `slow-ease-out`

| Parameter | Value |
|---|---|
| **cubic-bezier** | `0.4, 0, 0.2, 1` |
| **X1** | 0.4 |
| **Y1** | 0 |
| **X2** | 0.2 |
| **Y2** | 1 |

The animation starts slowly and gradually accelerates toward the end. It suits elements that are leaving the screen, creating a sense of rapid disappearance.

**Example use case:** closing a modal window (moving up or down).

### 2.4 `linear`

| Parameter | Value |
|---|---|
| **cubic-bezier** | `0.25, 0.25, 0.75, 0.75` |
| **X1** | 0.25 |
| **Y1** | 0.25 |
| **X2** | 0.75 |
| **Y2** | 0.75 |

An animation at constant speed, without acceleration or deceleration. Used for properties that must change uniformly, such as changes in opacity, color, or rotation.

**Important:** Linear animation is rarely suitable for moving objects, as it can look unnatural and robotic.

**Example use case:** a smooth change of color or opacity on a component.

### 2.5 How to choose a curve

| Scenario | Curve |
|---|---|
| Object **appears** from behind the screen or a mask (moving inward) | `slow-ease-out` |
| Object **exits** behind the screen or a mask (moving outward) | `accelerated-ease-in` |
| Object **moves within** the screen | `standard-ease-in-out` |
| Object **appears or disappears via opacity** (no positional movement) | `linear` |

---

## 3. Duration Primitives

**Duration:** sets the overall length of an animation, affecting its execution speed.

Collection `Motion/Primitives/Duration`. Durations are divided into three types: **Short**, **Medium**, **Long** — plus a reserved token `duration.custom`.

### 3.1 Short (100–200ms) — fast micro-interactions

Used for small elements, instant reactions, and simple states where a long animation would feel like a delay.

**Examples:** button press, tooltip appearance, icon swap.

| Token | Value |
|---|---|
| `duration.short1` | 100ms |
| `duration.short2` | 150ms |
| `duration.short3` | 200ms |

### 3.2 Medium (250–300ms) — standard transitions

Used for medium-scale transitions: showing/hiding components, state changes with visual weight.

**Examples:** dropdown, popover, chip, card.

| Token | Value |
|---|---|
| `duration.medium1` | 250ms |
| `duration.medium2` | 300ms |

### 3.3 Long (400–600ms) — large overlays

Used for large elements that occupy a significant portion of the screen and require a smoother transition.

**Examples:** bottom sheet, modal, drawer, full-screen overlay.

| Token | Value |
|---|---|
| `duration.long1` | 400ms |
| `duration.long2` | 500ms |
| `duration.long3` | 600ms |

### 3.0 How to choose a duration

| Range | Type | Usage | Examples |
|---|---|---|---|
| 100–200ms | Short | Small actions, instant reactions | Button press, badge, indicator |
| 200–400ms | Medium | Medium-scale transitions, component appearance | Tooltip, banner, popup, modal |
| 400ms+ | Long | Large overlays and complex transitions — use carefully to avoid irritation | Bottom sheet, carousel |

### 3.4 Custom

| Token | Value |
|---|---|
| `duration.custom` | Arbitrary value — only with approval from the design system team |

**Rules:**
1. Duration primitives are **not applied directly** — only through `Transforming/*` or `Patterns/*` presets.
2. Values `duration.long2` and `duration.long3` (500ms–600ms) — only for promo screens, onboarding, and skeleton animations.
3. `duration.custom` requires mandatory approval from the design system team.
4. To disable animation in `prefers-reduced-motion` mode, all durations are set to `0ms`.

---

## 4. Transforming Presets

**Transforming** — the change of an animation object's shape, color, and position.

Collection `Motion/Transforming`. Preset tokens: a combination of a Curves curve + Duration duration. They control how objects appear, move, or disappear.

### 4.0 Base Parameters (Base Patterns)

Each Transforming preset operates with one or more of the six base animation parameters. The parameters define **what exactly** is being transformed:

| Parameter | CSS property | Usage description |
|---|---|---|
| **Color** | `background-color`, `color`, `border-color` | Elements change color to draw attention or adapt to a new state. Example: changing a button's color on hover. |
| **Scale** | `transform: scale(...)` | Elements scale along the X and Y axes, adapting to new content — accommodating more or less content. Example: resizing a card or container, pressing a button. |
| **Position** | `transform: translate(...)` | Elements change position when content changes. Example: shifting elements on screen when other components appear or disappear. |
| **Opacity** | `opacity` | Used for a smooth appearance or disappearance of an element through transparency. |
| **Mask** | `clip-path` / `mask` | An element appears or disappears not all at once but in parts. Example: text appears from left to right, as if being gradually revealed. |
| **Rotation** | `transform: rotate(...)` | An object rotates around its axis. Example: loading icon animation, rotating arrow on a compass, icon rotation when opening a dropdown menu. |

> **Rule:** animate only `compositor-friendly` properties — `transform` and `opacity`. All other parameters (Color, Mask) — only for state transitions, not for enter/exit.

### 4.1 Group `Transforming/Instant` — instant reactions

| Token | Duration | Curve | CSS | Usage |
|---|---|---|---|---|
| `Transforming/Pressed` | `duration.short1` (100ms) | `accelerated-ease-in` | `transition: all 100ms cubic-bezier(0.5, 0, 0.75, 0)` | Press — instant reaction to touch/click |
| `Transforming/Released` | `duration.short2` (150ms) | `standard-ease-in-out` | `transition: all 150ms cubic-bezier(0.25, 1, 0.5, 1)` | Release — element returns to its initial state |

### 4.2 Group `Transforming/State` — state changes

| Token | Duration | Curve | CSS | Usage |
|---|---|---|---|---|
| `Transforming/State/Fast` | `duration.short2` (150ms) | `slow-ease-out` | `transition: all 150ms cubic-bezier(0.4, 0, 0.2, 1)` | Hover, focus, checked, disabled |
| `Transforming/State/Default` | `duration.short3` (200ms) | `slow-ease-out` | `transition: all 200ms cubic-bezier(0.4, 0, 0.2, 1)` | Standard component state transition |
| `Transforming/State/Slow` | `duration.medium2` (300ms) | `slow-ease-out` | `transition: all 300ms cubic-bezier(0.4, 0, 0.2, 1)` | Transitions with size change, complex state-change |

### 4.3 Group `Transforming/Enter` — element appearance

| Token | Duration | Curve | CSS | Usage |
|---|---|---|---|---|
| `Transforming/Enter/Fast` | `duration.short2` (150ms) | `standard-ease-in-out` | `transition: all 150ms cubic-bezier(0.25, 1, 0.5, 1)` | Toast, tooltip, badge |
| `Transforming/Enter/Default` | `duration.medium1` (250ms) | `standard-ease-in-out` | `transition: all 250ms cubic-bezier(0.25, 1, 0.5, 1)` | Dropdown, popover, chip |
| `Transforming/Enter/Slow` | `duration.long1` (400ms) | `standard-ease-in-out` | `transition: all 400ms cubic-bezier(0.25, 1, 0.5, 1)` | Bottom sheet, modal, drawer |

### 4.4 Group `Transforming/Exit` — element dismissal

| Token | Duration | Curve | CSS | Usage |
|---|---|---|---|---|
| `Transforming/Exit/Fast` | `duration.short1` (100ms) | `accelerated-ease-in` | `transition: all 100ms cubic-bezier(0.5, 0, 0.75, 0)` | Toast dismiss, tooltip hide |
| `Transforming/Exit/Default` | `duration.short3` (200ms) | `accelerated-ease-in` | `transition: all 200ms cubic-bezier(0.5, 0, 0.75, 0)` | Dropdown close, popover hide |
| `Transforming/Exit/Slow` | `duration.medium2` (300ms) | `accelerated-ease-in` | `transition: all 300ms cubic-bezier(0.5, 0, 0.75, 0)` | Bottom sheet dismiss, modal close |

---

## 5. Patterns Presets

Collection `Motion/Patterns`. Ready-to-use reusable animation solutions for specific components and scenarios. Each preset includes a specification for **appear** and **hide**.

**Role of presets:**
- Presets are used to create consistent animation for individual elements and components, but teams can create their own.
- Presets are created using a combination of Curves, Duration, and Transforming primitives.
- **Base Patterns** — base patterns serve as presets for animating individual interface elements of various sizes and contexts.

### 5.1 Scale Short

Used for animating the appearance and hiding of small interface elements — for example, badges or notifications — that appear in a fixed location, such as in the corner of an Avatar component. Suitable for small elements (chip, badge, tag) that appear and hide through a scale change.

| Action | Curve | Duration | CSS |
|---|---|---|---|
| **Appear** | `standard-ease-in-out` | `duration.short1` (100ms) | `transition: transform 100ms cubic-bezier(0.25, 1, 0.5, 1)` |
| **Hide** | `standard-ease-in-out` | `duration.short3` (200ms) | `transition: transform 200ms cubic-bezier(0.25, 1, 0.5, 1)` |

```css
/* Appear */
.scale-short-appear { transform: scale(0) → scale(1); transition: transform 100ms cubic-bezier(0.25, 1, 0.5, 1); }
/* Hide */
.scale-short-hide   { transform: scale(1) → scale(0); transition: transform 200ms cubic-bezier(0.25, 1, 0.5, 1); }
```

### 5.2 Position

Positional shift for large elements (bottom sheet, drawer, modal). Appears slowly — draws attention; hides quickly — does not delay the user.

| Action | Curve | Duration | CSS |
|---|---|---|---|
| **Appear** | `slow-ease-out` | `duration.long1` (400ms) | `transition: transform 400ms cubic-bezier(0.4, 0, 0.2, 1)` |
| **Disappear** | `accelerated-ease-in` | `duration.long1` (400ms) | `transition: transform 400ms cubic-bezier(0.5, 0, 0.75, 0)` |

```css
/* Appear */
.position-appear    { transform: translateY(100%) → translateY(0); transition: transform 400ms cubic-bezier(0.4, 0, 0.2, 1); }
/* Disappear */
.position-disappear { transform: translateY(0) → translateY(100%); transition: transform 400ms cubic-bezier(0.5, 0, 0.75, 0); }
```

### 5.3 Position Short

Fast positional shift for small elements (toast, snackbar, tooltip with a position).

| Action | Curve | Duration | CSS |
|---|---|---|---|
| **Appear** | `slow-ease-out` | `duration.short2` (150ms) | `transition: transform 150ms cubic-bezier(0.4, 0, 0.2, 1)` |
| **Hide** | `accelerated-ease-in` | `duration.short2` (150ms) | `transition: transform 150ms cubic-bezier(0.5, 0, 0.75, 0)` |

```css
/* Appear */
.position-short-appear { transform: translateY(8px) → translateY(0); transition: transform 150ms cubic-bezier(0.4, 0, 0.2, 1); }
/* Hide */
.position-short-hide   { transform: translateY(0) → translateY(8px); transition: transform 150ms cubic-bezier(0.5, 0, 0.75, 0); }
```

### 5.4 Mask

Appearance/hiding via masking (clip-path). Used for elements that unfold like a "curtain": menus, panels, expanding sections.

| Action | Curve | Duration | CSS |
|---|---|---|---|
| **Appear** | `slow-ease-out` | `duration.medium1` (250ms) | `transition: clip-path 250ms cubic-bezier(0.4, 0, 0.2, 1)` |
| **Hide** | `accelerated-ease-in` | `duration.medium1` (250ms) | `transition: clip-path 250ms cubic-bezier(0.5, 0, 0.75, 0)` |

```css
/* Appear */
.mask-appear { clip-path: inset(0 0 100% 0) → inset(0 0 0% 0); transition: clip-path 250ms cubic-bezier(0.4, 0, 0.2, 1); }
/* Hide */
.mask-hide   { clip-path: inset(0 0 0% 0) → inset(0 0 100% 0); transition: clip-path 250ms cubic-bezier(0.5, 0, 0.75, 0); }
```

### 5.5 Scale and Opacity

Combined pattern: scale + opacity simultaneously. Creates a sense of the element "materializing." Applied to dropdown, popover, and context menus.

| Action | Parameters | Curve | Duration | CSS |
|---|---|---|---|---|
| **Appear** | opacity `0→100%`, scale `50→100%` | `standard-ease-in-out` | `duration.medium1` (250ms) | `transition: opacity 250ms, transform 250ms cubic-bezier(0.25, 1, 0.5, 1)` |
| **Hide** | opacity `100→0%`, scale `100→50%` | `standard-ease-in-out` | `duration.medium1` (250ms) | `transition: opacity 250ms, transform 250ms cubic-bezier(0.25, 1, 0.5, 1)` |

```css
/* Appear */
.scale-opacity-appear {
  opacity: 0; transform: scale(0.5);
  transition: opacity 250ms cubic-bezier(0.25, 1, 0.5, 1),
              transform 250ms cubic-bezier(0.25, 1, 0.5, 1);
}
.scale-opacity-appear.active { opacity: 1; transform: scale(1); }

/* Hide */
.scale-opacity-hide {
  opacity: 1; transform: scale(1);
  transition: opacity 250ms cubic-bezier(0.25, 1, 0.5, 1),
              transform 250ms cubic-bezier(0.25, 1, 0.5, 1);
}
.scale-opacity-hide.hidden { opacity: 0; transform: scale(0.5); }
```

### 5.6 Color

Smooth color change without geometry modification. Applied for theme switching, background state changes, and hover effects on color blocks.

| Action | Curve | Duration | CSS |
|---|---|---|---|
| **Appear** | `linear` | `duration.short3` (200ms) | `transition: color 200ms cubic-bezier(0.25, 0.25, 0.75, 0.75), background-color 200ms cubic-bezier(0.25, 0.25, 0.75, 0.75)` |
| **Hide** | `linear` | `duration.short3` (200ms) | same |

```css
.color-transition {
  transition: color 200ms cubic-bezier(0.25, 0.25, 0.75, 0.75),
              background-color 200ms cubic-bezier(0.25, 0.25, 0.75, 0.75),
              border-color 200ms cubic-bezier(0.25, 0.25, 0.75, 0.75);
}
```

### 5.7 Scale Medium

Scaling with a longer duration. For medium and large elements that require smoothness (cards, images, illustrations).

| Action | Curve | Duration | CSS |
|---|---|---|---|
| **Appear** | `standard-ease-in-out` | `duration.long1` (400ms) | `transition: transform 400ms cubic-bezier(0.25, 1, 0.5, 1)` |
| **Hide** | `standard-ease-in-out` | `duration.long1` (400ms) | `transition: transform 400ms cubic-bezier(0.25, 1, 0.5, 1)` |

```css
/* Appear */
.scale-medium-appear { transform: scale(0) → scale(1); transition: transform 400ms cubic-bezier(0.25, 1, 0.5, 1); }
/* Hide */
.scale-medium-hide   { transform: scale(1) → scale(0); transition: transform 400ms cubic-bezier(0.25, 1, 0.5, 1); }
```

### 5.8 Position Medium

Medium-speed positional shift. For elements that appear more smoothly than Position Short but faster than Position. Suitable for navigation panels, sidebars, and inline-expanding blocks.

| Action | Curve | Duration | CSS |
|---|---|---|---|
| **Appear** | `standard-ease-in-out` | `duration.long1` (400ms) | `transition: transform 400ms cubic-bezier(0.25, 1, 0.5, 1)` |
| **Hide** | `standard-ease-in-out` | `duration.long1` (400ms) | `transition: transform 400ms cubic-bezier(0.25, 1, 0.5, 1)` |

```css
/* Appear */
.position-medium-appear { transform: translateX(-100%) → translateX(0); transition: transform 400ms cubic-bezier(0.25, 1, 0.5, 1); }
/* Hide */
.position-medium-hide   { transform: translateX(0) → translateX(-100%); transition: transform 400ms cubic-bezier(0.25, 1, 0.5, 1); }
```

---

## 5.9 Loop Patterns — looping animations

Used only in special scenarios: loading, promo, onboarding.

### Shimmer (Skeleton)

Used in `Skeleton/*` components. Tied to `Skeleton/Wave` from color-rules.md.

| Parameter | Value |
|---|---|
| **Token** | `Patterns/Shimmer` |
| **Duration** | `1200ms` |
| **Curve** | `linear` |
| **Iteration** | `infinite` |
| **Keyframes** | `0%: translateX(-100%)` → `100%: translateX(100%)` |
| **CSS** | `animation: shimmer 1200ms cubic-bezier(0.25, 0.25, 0.75, 0.75) infinite` |

```css
@keyframes shimmer {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(100%); }
}
```

### Pulse

Pulsation for badge indicators, online statuses, and notification dots.

| Parameter | Value |
|---|---|
| **Token** | `Patterns/Pulse` |
| **Duration** | `1500ms` |
| **Curve** | `slow-ease-out` |
| **Iteration** | `infinite` |
| **Direction** | `alternate` |
| **Keyframes** | `0%: opacity 1, scale 1` → `100%: opacity 0.4, scale 0.88` |
| **CSS** | `animation: pulse 1500ms cubic-bezier(0.4, 0, 0.2, 1) infinite alternate` |

```css
@keyframes pulse {
  0%   { opacity: 1; transform: scale(1); }
  100% { opacity: 0.4; transform: scale(0.88); }
}
```

### Spin

Rotation for loading indicators.

| Parameter | Value |
|---|---|
| **Token** | `Patterns/Spin` |
| **Duration** | `800ms` |
| **Curve** | `linear` |
| **Iteration** | `infinite` |
| **CSS** | `animation: spin 800ms cubic-bezier(0.25, 0.25, 0.75, 0.75) infinite` |

```css
@keyframes spin {
  0%   { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}
```

### FadeIn / FadeOut

Appearance and disappearance via opacity — for overlays and backdrops.

| Parameter | FadeIn | FadeOut |
|---|---|---|
| **Token** | `Patterns/FadeIn` | `Patterns/FadeOut` |
| **Duration** | `duration.short3` (200ms) | `duration.short2` (150ms) |
| **Curve** | `standard-ease-in-out` | `accelerated-ease-in` |
| **CSS** | `animation: fadeIn 200ms cubic-bezier(0.25, 1, 0.5, 1) both` | `animation: fadeOut 150ms cubic-bezier(0.5, 0, 0.75, 0) both` |

```css
@keyframes fadeIn  { from { opacity: 0; } to { opacity: 1; } }
@keyframes fadeOut { from { opacity: 1; } to { opacity: 0; } }
```

---

## 6. Ready-made component animations

A collection of ready-made animation solutions defined in the design system. They cover three scenarios: **press reaction**, **value selection**, and **appearance/hiding** of components.

> **Platform specifics:** Android uses the `ripple` effect instead of scale on press. iOS — background color change. The CSS implementations below describe web behavior.

---

### 6.1 Press animation

There are 3 types of animations for components on press.

#### `pushHighlight`

For components **without scale** on press. Reaction only through color (web) / ripple (Android) / color change (iOS).

**Example:** Cell, list, table rows.

| Action | Curve | Duration | CSS |
|---|---|---|---|
| **Press** | `linear` | `duration.short2` (150ms) | `transition: background-color 150ms cubic-bezier(0.25, 0.25, 0.75, 0.75)` |
| **Release** | `linear` | `duration.short2` (150ms) | `transition: background-color 150ms cubic-bezier(0.25, 0.25, 0.75, 0.75)` |

```css
.push-highlight {
  transition: background-color 150ms cubic-bezier(0.25, 0.25, 0.75, 0.75);
}
.push-highlight:active { background-color: var(--color-pressed-state); }
```

#### `pushItem`

For **medium and large** components. The press is visualized through a scale reduction to 95%.

**Example:** Banner, card, large list item.

| Action | Parameters | Curve | Duration | CSS |
|---|---|---|---|---|
| **Press** | scale in 100% → 95% | `standard-ease-in-out` | `duration.short3` (200ms) | `transition: transform 200ms cubic-bezier(0.25, 1, 0.5, 1)` |
| **Release** | scale out 95% → 100% | `standard-ease-in-out` | `duration.short3` (200ms) | `transition: transform 200ms cubic-bezier(0.25, 1, 0.5, 1)` |

```css
.push-item {
  transition: transform 200ms cubic-bezier(0.25, 1, 0.5, 1);
}
.push-item:active { transform: scale(0.95); }
```

#### `pushButton`

A combination of `pushHighlight` + `pushItem`. On press, **scale and color change simultaneously**.

**Example:** Button (all variants).

| Action | Parameters | Curve | Duration | CSS |
|---|---|---|---|---|
| **Press** | scale in 100% → 95% + color change | `standard-ease-in-out` | `duration.short3` (200ms) | `transition: transform 200ms, background-color 200ms cubic-bezier(0.25, 1, 0.5, 1)` |
| **Release** | scale out 95% → 100% + color restore | `standard-ease-in-out` | `duration.short3` (200ms) | same |

```css
.push-button {
  transition: transform 200ms cubic-bezier(0.25, 1, 0.5, 1),
              background-color 200ms cubic-bezier(0.25, 1, 0.5, 1);
}
.push-button:active {
  transform: scale(0.95);
  background-color: var(--color-pressed-state);
}
```

---

### 6.2 Appearance animation

Three patterns for component appearance on screen — by element size.

#### `scaleS`

For **small** elements. Appears and hides through scale.

**Example:** Badge, status indicator, notification counter.

| Action | Parameters | Curve | Duration | CSS |
|---|---|---|---|---|
| **Appear** | scale 0% → 100% | `standard-ease-in-out` | `duration.short3` (200ms) | `transition: transform 200ms cubic-bezier(0.25, 1, 0.5, 1)` |
| **Hide** | scale 100% → 0% | `standard-ease-in-out` | `duration.short3` (200ms) | `transition: transform 200ms cubic-bezier(0.25, 1, 0.5, 1)` |

```css
.scale-s-enter { transform: scale(0); }
.scale-s-enter-active {
  transform: scale(1);
  transition: transform 200ms cubic-bezier(0.25, 1, 0.5, 1);
}
.scale-s-exit { transform: scale(1); }
.scale-s-exit-active {
  transform: scale(0);
  transition: transform 200ms cubic-bezier(0.25, 1, 0.5, 1);
}
```

#### `fadeM`

For **medium** elements. Combines opacity + scale — creates a sense of "materialization."

**Example:** Tooltip, Banner, Popover.

| Action | Parameters | Curve | Duration | CSS |
|---|---|---|---|---|
| **Appear** | opacity 0→100% + scale 50→100% | `standard-ease-in-out` | `duration.medium1` (250ms) | `transition: opacity 250ms, transform 250ms cubic-bezier(0.25, 1, 0.5, 1)` |
| **Hide** | opacity 100→0% + scale 100→50% | `standard-ease-in-out` | `duration.medium1` (250ms) | same |

```css
.fade-m-enter { opacity: 0; transform: scale(0.5); }
.fade-m-enter-active {
  opacity: 1;
  transform: scale(1);
  transition: opacity 250ms cubic-bezier(0.25, 1, 0.5, 1),
              transform 250ms cubic-bezier(0.25, 1, 0.5, 1);
}
.fade-m-exit { opacity: 1; transform: scale(1); }
.fade-m-exit-active {
  opacity: 0;
  transform: scale(0.5);
  transition: opacity 250ms cubic-bezier(0.25, 1, 0.5, 1),
              transform 250ms cubic-bezier(0.25, 1, 0.5, 1);
}
```

#### `moveM`

For **medium** elements **appearing from outside the screen**. Asymmetric curves: smooth appearance, fast hiding.

**Example:** Snackbar, Toast positioned from the edge of the screen.

| Action | Curve | Duration | CSS |
|---|---|---|---|
| **Appear** | `slow-ease-out` | `duration.long1` (400ms) | `transition: transform 400ms cubic-bezier(0.4, 0, 0.2, 1)` |
| **Hide** | `accelerated-ease-in` | `duration.long1` (400ms) | `transition: transform 400ms cubic-bezier(0.5, 0, 0.75, 0)` |

```css
.move-m-enter { transform: translateY(100%); }
.move-m-enter-active {
  transform: translateY(0);
  transition: transform 400ms cubic-bezier(0.4, 0, 0.2, 1);
}
.move-m-exit { transform: translateY(0); }
.move-m-exit-active {
  transform: translateY(100%);
  transition: transform 400ms cubic-bezier(0.5, 0, 0.75, 0);
}
```

---

### 6.3 Selection animation

Animation for committing a value on a row of discrete elements (rating stars, dots, steps).

#### `bounceWave`

Tapping element N replays the fill from the very first element: elements 1…N animate as a
sequential wave — each starts one `step` after the previous, bounces up and reveals its fill
(`clip-path` left→right) at the bounce peak. Elements above N reset instantly — no transition.
The companion hover behaviour previews the value: the hovered element scales up while the fills
up to it apply instantly.

**Example:** Rating (input mode) — the star wave.

| Phase | Parameters | Curve | Duration | CSS |
|---|---|---|---|---|
| **Wave stagger** | element *i* starts at (*i* − 1) × step | — | `duration.custom` (70ms) | JS-scheduled per-element delay |
| **Bounce** | `transform: scale(1 → 1.28 → 1)`, peak at 110ms | `standard-ease-in-out` | `duration.custom` (380ms) | Web Animations API keyframes, `cubic-bezier(0.25, 1, 0.5, 1)` |
| **Fill reveal** | `clip-path` left→right, fires at the peak | `slow-ease-out` | `duration.custom` (160ms) | `transition: clip-path 160ms cubic-bezier(0.4, 0, 0.2, 1)` |
| **Companion hover** | hovered element `transform: scale(1.18)`; fill preview instant | `slow-ease-out` | `Transforming/State/Fast` (150ms) | `transition: transform var(--transforming-state-fast)` |

Durations are `duration.custom` (70 / 380 / 160ms — approved for this preset, see § 3.4): the
bounce deliberately sits between `medium2` and `long1` so consecutive bounces stay readable at
the 70ms stagger.

```css
:root {
  --component-bounce-wave-step:   70ms;   /* per-element stagger */
  --component-bounce-wave-bounce: 380ms;  /* scale 1 → 1.28 → 1, peak at 110ms */
  --component-bounce-wave-fill:   160ms;  /* clip-path reveal, fires at the peak */
}
```

The wave is JS-orchestrated (per-element delays + keyframes); all timing is read from the props
above, so `prefers-reduced-motion` zeroes the whole preset (§ 7.1) — the driver skips the wave at
`0ms` and applies fills instantly. Reference implementation: Rating — `src/shared/shared.css`
(props) + `src/rating.njk` (wave driver); spec §§ 10–11.

---

## 7. Token-to-component mapping table

### 6.1 Buttons

| State | Token | Property |
|---|---|---|
| Hover | `Transforming/State/Fast` | `background-color`, `border-color` |
| Pressed (down) | `Transforming/Pressed` | `background-color`, `transform: scale(0.97)` |
| Pressed (up) | `Transforming/Released` | `background-color`, `transform: scale(1)` |
| Disabled | `Transforming/State/Default` | `opacity`, `background-color` |
| Focus visible | `Transforming/State/Fast` | `box-shadow` (focus ring) |

### 6.2 Input / Select

| State | Token | Property |
|---|---|---|
| Focus | `Transforming/State/Default` | `border-color`, `box-shadow` |
| Error | `Transforming/State/Default` | `border-color`, `color` |
| Disabled | `Transforming/State/Default` | `opacity`, `background-color` |
| Hint text appears | `Transforming/Enter/Fast` | `opacity`, `transform: translateY(-4px → 0)` |

### 6.3 Bottom Sheet / Modal

| Event | Token | Property |
|---|---|---|
| Appear | `Transforming/Enter/Slow` | `transform: translateY(100% → 0)` |
| Close | `Transforming/Exit/Slow` | `transform: translateY(0 → 100%)` |
| Backdrop appear | `Patterns/FadeIn` | `opacity` |
| Backdrop disappear | `Patterns/FadeOut` | `opacity` |

### 6.4 Toast / Snackbar

| Event | Token | Property |
|---|---|---|
| Appear | `Transforming/Enter/Fast` | `opacity`, `transform: translateY(8px → 0)` |
| Auto-dismiss | `Transforming/Exit/Fast` | `opacity`, `transform: translateY(0 → -8px)` |

### 6.5 Dropdown / Popover

| Event | Token | Property |
|---|---|---|
| Open | `Transforming/Enter/Default` | `opacity`, `transform: scaleY(0.92 → 1)` |
| Close | `Transforming/Exit/Default` | `opacity`, `transform: scaleY(1 → 0.92)` |

### 6.6 Checkbox / Switch

| State | Token | Property |
|---|---|---|
| Unchecked → Checked | `Transforming/State/Default` | `background-color`, `transform` (checkmark) |
| Checked → Unchecked | `Transforming/State/Default` | `background-color`, `opacity` |
| Switch thumb | `Transforming/State/Default` | `transform: translateX(...)` |

### 6.7 Skeleton

| State | Token |
|---|---|
| Shimmer wave | `Patterns/Shimmer` |
| Skeleton → Content | `Transforming/Enter/Default` (content fade-in) |

### 6.8 Rating (input mode)

| State | Token | Property |
|---|---|---|
| Hover | `bounceWave` companion hover (`Transforming/State/Fast`) | `transform: scale(1.18)` + instant fill preview |
| Pressed (down/up) | `pushItem` | `transform: scale(0.95)` → `scale(1)` |
| Tap — commit value | `bounceWave` | staggered `transform` bounce + `clip-path` fill reveal |

---

## 8. CSS Custom Properties

Generated — this listing mirrors `tokens/generated/motion.css`. Edit `tokens/src/motion/*.json` and
rebuild rather than editing CSS anywhere.

```css
:root {
  /* ── Curves primitives ── */
  --curve-accelerated-ease-in:  cubic-bezier(0.5, 0, 0.75, 0);
  --curve-standard-ease-in-out: cubic-bezier(0.25, 1, 0.5, 1);
  --curve-slow-ease-out:        cubic-bezier(0.4, 0, 0.2, 1);
  --curve-linear:               cubic-bezier(0.25, 0.25, 0.75, 0.75);

  /* ── Duration primitives ── */
  /* Short: 100–200ms */
  --duration-short1: 100ms;
  --duration-short2: 150ms;
  --duration-short3: 200ms;
  /* Medium: 250–300ms */
  --duration-medium1: 250ms;
  --duration-medium2: 300ms;
  /* Long: 400–600ms */
  --duration-long1: 400ms;
  --duration-long2: 500ms;
  --duration-long3: 600ms;
  /* Custom (use only with design system approval) */
  /* --duration-custom: Xms; */

  /* ── Transforming tokens ── */
  --transforming-pressed:        100ms cubic-bezier(0.5, 0, 0.75, 0);
  --transforming-released:       150ms cubic-bezier(0.25, 1, 0.5, 1);

  --transforming-state-fast:     150ms cubic-bezier(0.4, 0, 0.2, 1);
  --transforming-state-default:  200ms cubic-bezier(0.4, 0, 0.2, 1);
  --transforming-state-slow:     300ms cubic-bezier(0.4, 0, 0.2, 1);

  --transforming-enter-fast:     150ms cubic-bezier(0.25, 1, 0.5, 1);
  --transforming-enter-default:  250ms cubic-bezier(0.25, 1, 0.5, 1);
  --transforming-enter-slow:     400ms cubic-bezier(0.25, 1, 0.5, 1);

  --transforming-exit-fast:      100ms cubic-bezier(0.5, 0, 0.75, 0);
  --transforming-exit-default:   200ms cubic-bezier(0.5, 0, 0.75, 0);
  --transforming-exit-slow:      300ms cubic-bezier(0.5, 0, 0.75, 0);

  /* ── Pattern tokens — appear/hide ── */
  --pattern-scale-short-appear:         100ms cubic-bezier(0.25, 1, 0.5, 1);
  --pattern-scale-short-hide:           200ms cubic-bezier(0.25, 1, 0.5, 1);

  --pattern-position-appear:            400ms cubic-bezier(0.4, 0, 0.2, 1);
  --pattern-position-disappear:         400ms cubic-bezier(0.5, 0, 0.75, 0);

  --pattern-position-short-appear:      150ms cubic-bezier(0.4, 0, 0.2, 1);
  --pattern-position-short-hide:        150ms cubic-bezier(0.5, 0, 0.75, 0);

  --pattern-mask-appear:                250ms cubic-bezier(0.4, 0, 0.2, 1);
  --pattern-mask-hide:                  250ms cubic-bezier(0.5, 0, 0.75, 0);

  --pattern-scale-opacity-appear:       250ms cubic-bezier(0.25, 1, 0.5, 1);
  --pattern-scale-opacity-hide:         250ms cubic-bezier(0.25, 1, 0.5, 1);

  --pattern-color:                      200ms cubic-bezier(0.25, 0.25, 0.75, 0.75);

  --pattern-scale-medium-appear:        400ms cubic-bezier(0.25, 1, 0.5, 1);
  --pattern-scale-medium-hide:          400ms cubic-bezier(0.25, 1, 0.5, 1);

  --pattern-position-medium-appear:     400ms cubic-bezier(0.25, 1, 0.5, 1);
  --pattern-position-medium-hide:       400ms cubic-bezier(0.25, 1, 0.5, 1);

  /* ── Component animation tokens ── */
  /* Press animations */
  --component-push-highlight:    150ms cubic-bezier(0.25, 0.25, 0.75, 0.75);
  --component-push-item-press:   200ms cubic-bezier(0.25, 1, 0.5, 1);
  --component-push-item-release: 200ms cubic-bezier(0.25, 1, 0.5, 1);
  --component-push-button-press: 200ms cubic-bezier(0.25, 1, 0.5, 1);
  --component-push-button-release: 200ms cubic-bezier(0.25, 1, 0.5, 1);

  /* Appearance animations */
  --component-scale-s-appear:    200ms cubic-bezier(0.25, 1, 0.5, 1);
  --component-scale-s-hide:      200ms cubic-bezier(0.25, 1, 0.5, 1);
  --component-fade-m-appear:     250ms cubic-bezier(0.25, 1, 0.5, 1);
  --component-fade-m-hide:       250ms cubic-bezier(0.25, 1, 0.5, 1);
  --component-move-m-appear:     400ms cubic-bezier(0.4, 0, 0.2, 1);
  --component-move-m-hide:       400ms cubic-bezier(0.5, 0, 0.75, 0);

  /* Selection animations (bounceWave — durations only; curves are fixed by the preset, § 6.3) */
  --component-bounce-wave-step:   70ms;
  --component-bounce-wave-bounce: 380ms;
  --component-bounce-wave-fill:   160ms;

  /* ── Loop pattern tokens ── */
  --pattern-shimmer:  shimmer 1200ms cubic-bezier(0.25, 0.25, 0.75, 0.75) infinite;
  --pattern-pulse:    pulse 1500ms cubic-bezier(0.4, 0, 0.2, 1) infinite alternate;
  --pattern-spin:     spin 800ms cubic-bezier(0.25, 0.25, 0.75, 0.75) infinite;
  --pattern-fade-in:  fadeIn 200ms cubic-bezier(0.25, 1, 0.5, 1) both;
  --pattern-fade-out: fadeOut 150ms cubic-bezier(0.5, 0, 0.75, 0) both;
}
```

### 7.1 Reduced Motion

```css
@media (prefers-reduced-motion: reduce) {
  :root {
    --transforming-pressed:       0ms linear;
    --transforming-released:      0ms linear;
    --transforming-state-fast:    0ms linear;
    --transforming-state-default: 0ms linear;
    --transforming-state-slow:    0ms linear;
    --transforming-enter-fast:    0ms linear;
    --transforming-enter-default: 0ms linear;
    --transforming-enter-slow:    0ms linear;
    --transforming-exit-fast:     0ms linear;
    --transforming-exit-default:  0ms linear;
    --transforming-exit-slow:     0ms linear;

    --pattern-scale-short-appear:         0ms linear;
    --pattern-scale-short-hide:           0ms linear;
    --pattern-position-appear:            0ms linear;
    --pattern-position-disappear:         0ms linear;
    --pattern-position-short-appear:      0ms linear;
    --pattern-position-short-hide:        0ms linear;
    --pattern-mask-appear:                0ms linear;
    --pattern-mask-hide:                  0ms linear;
    --pattern-scale-opacity-appear:       0ms linear;
    --pattern-scale-opacity-hide:         0ms linear;
    --pattern-color:                      0ms linear;
    --pattern-scale-medium-appear:        0ms linear;
    --pattern-scale-medium-hide:          0ms linear;
    --pattern-position-medium-appear:     0ms linear;
    --pattern-position-medium-hide:       0ms linear;

    --pattern-shimmer:            none;
    --pattern-pulse:              none;
    --pattern-spin:               none;
    --pattern-fade-in:            none;
    --pattern-fade-out:           none;

    --component-push-highlight:   0ms linear;
    --component-push-item-press:    0ms linear;
    --component-push-item-release:  0ms linear;
    --component-push-button-press:   0ms linear;
    --component-push-button-release: 0ms linear;
    --component-scale-s-appear:   0ms linear;
    --component-scale-s-hide:     0ms linear;
    --component-fade-m-appear:    0ms linear;
    --component-fade-m-hide:      0ms linear;
    --component-move-m-appear:    0ms linear;
    --component-move-m-hide:      0ms linear;
    --component-bounce-wave-step:   0ms;
    --component-bounce-wave-bounce: 0ms;
    --component-bounce-wave-fill:   0ms;
  }
}
```

---

## 9. Token selection decision tree

1. **Press reaction?** → `Transforming/Pressed` (down) + `Transforming/Released` (up).
2. **Committing a value on a row of discrete elements (rating stars, dots, steps)?** → `bounceWave` (§ 6.3).
3. **Visual state change (hover, focus, checked, disabled)?** → `Transforming/State/Fast` or `/Default`.
4. **Element appearing on screen?** → `Transforming/Enter/Fast` (small) / `/Default` (medium) / `/Slow` (large overlay).
5. **Element leaving the screen?** → `Transforming/Exit/Fast` / `/Default` / `/Slow`.
6. **Loading (skeleton wave)?** → `Patterns/Shimmer`.
7. **Loading spinner?** → `Patterns/Spin`.
8. **Pulsing indicator?** → `Patterns/Pulse`.
9. **Backdrop/overlay without movement?** → `Patterns/FadeIn` / `Patterns/FadeOut`.

---

## 10. Performance

Animate only **compositor-friendly** properties:

| Property | Performance | Used for |
|---|---|---|
| `transform` | ✅ Compositor | Movement, scale, rotation |
| `opacity` | ✅ Compositor | Appearance, disappearance |
| `filter` | ⚠️ GPU-heavy | Shimmer effect only |
| `background-color` | ⚠️ Repaint | State transitions only |
| `border-color` | ⚠️ Repaint | State transitions only |
| `box-shadow` | ⚠️ Repaint | Focus ring, elevation only |
| `width` / `height` | ❌ Reflow | Do not animate — use `transform` |
| `top` / `left` | ❌ Reflow | Do not animate — use `transform` |

---

## 11. Accessibility (a11y)

1. `prefers-reduced-motion: reduce` — all `--transforming-*` are set to `0ms`, all `--pattern-*` are disabled (see section 7.1).
2. Animation carries no semantic meaning — important information is conveyed through text or icons, not movement alone.
3. Skeleton with `Patterns/Shimmer` requires `aria-busy="true"` and `aria-label="Loading"`.
4. Spinner with `Patterns/Spin` requires `role="status"` and `aria-label="Loading"`.

---

## 12. What is prohibited

- Hardcoding `transition: 0.3s ease` — only through a token.
- Applying `curve-linear` for moving objects — only for opacity, color, rotation.
- Applying `Duration > 600ms` in everyday UI (not promo, not onboarding).
- Ignoring `prefers-reduced-motion` — all tokens must be overridden to `0ms`.
- Animating `width` or `height` directly — use `transform: scaleX/scaleY`.
- Applying Patterns outside their designated components.
- Applying Curves or Duration primitives directly in components — only through Transforming or Patterns.
- Creating custom animations without approval from the design system team.

---

## 13. Ready-Go checklist before publishing a component

- [ ] All state transitions use `Transforming/State/*`.
- [ ] Pressed/Released — `Transforming/Pressed` and `Transforming/Released`.
- [ ] Enter/Exit — Fast/Default/Slow selected according to element size.
- [ ] No hardcoded `transition: Xms ease` — only CSS variables.
- [ ] Only compositor-friendly properties animated (`transform`, `opacity`).
- [ ] `@media (prefers-reduced-motion: reduce)` added with `0ms` for all tokens.
- [ ] Skeleton uses `Patterns/Shimmer` + `aria-busy="true"`.
- [ ] Spinner uses `Patterns/Spin` + `role="status"`.
- [ ] Durations > 600ms only in promo/onboarding.
- [ ] Custom animation approved by the design system team.

---

## 14. History log

| Version | Date | Author | Change |
|---|---|---|---|
| 1.6.0 | 2026-07-24 | Eugene Beloussov | **`bounceWave` selection animation catalogued** (new § 6.3): committing a value on a row of discrete elements replays the fill from element 1 as a sequential wave — `duration.custom` 70ms stagger, scale 1→1.28→1 bounce over 380ms (`standard-ease-in-out`, peak 110ms), `clip-path` fill reveal 160ms (`slow-ease-out`) at the peak; companion hover = scale ×1.18 via `Transforming/State/Fast` + instant fill preview. New CSS props `--component-bounce-wave-step/-bounce/-fill` (§ 8) + reduced-motion overrides (§ 7.1); decision-tree entry (§ 9); § 7 mapping gains 6.8 Rating. Promoted from the Rating ⚠️ un-catalogued TODO (spec § 10); Rating is the reference implementation. Header version realigned to the History log (it had lagged at 1.3.0 while 1.4.0/1.5.0 rows already existed). |
| 1.0.0 | 2026-04-23 | Eugene Beloussov | First version of motion rules. Duration primitives (12 steps) and Easing (8 curves), semantic Transition tokens (14 tokens), Animation presets (Shimmer, Pulse, Spin, FadeIn, FadeOut). CSS Custom Properties, reduced-motion, iOS/Android mapping. |
| 1.1.0 | 2026-04-23 | Eugene Beloussov | Values from source applied. Token structure brought to 4 categories: Curves, Duration, Transforming, Patterns. Curves renamed and replaced with exact values from the design system: `accelerated-ease-in` (0.5, 0, 0.75, 0), `standard-ease-in-out` (0.25, 1, 0.5, 1), `slow-ease-out` (0.4, 0, 0.2, 1), `linear` (0.25, 0.25, 0.75, 0.75). Principles expanded to 4 (added Naturalness, Consistency, Moderation, Context section). "Tokens and presets" section added with structure description, usage rules, and development recommendations. CSS variables renamed to match the new structure. iOS/Android mapping removed (moved to platform documentation). |
| 1.7.0 | 2026-08-03 | Eugene Beloussov | **Motion moved into the token pipeline.** DTCG sources at `tokens/src/motion/` (primitives, presets, loops) → `tokens/generated/motion.css`, built and validated with every other category. Presets are composed by reference — a Transforming or Pattern token cites `{duration.*}` and `{curve.*}` instead of repeating literals — so 57 of the 58 listed properties are now generated from 12 primitives (`--duration-custom` stays commented out by design). The five loop patterns emit their `@keyframes` alongside the token, since `--pattern-shimmer`, `--pattern-pulse`, `--pattern-fade-in` and `--pattern-fade-out` had named keyframes nobody defined. Fixes the §7.1 block, which zeroed `--component-push-item` and `--component-push-button` — neither exists; the real names carry `-press` / `-release`. Components no longer declare motion tokens locally or carry their own reduced-motion overrides. |
| 1.5.0 | 2026-04-23 | Eugene Beloussov | Documentation supplemented from Figma: §3 Duration — introductory paragraph added. §4 Transforming — introductory paragraph added "Transforming is the change of shape, color, and position"; §4.0 Base Params expanded with usage descriptions for each of 6 parameters (Color, Scale, Position, Opacity, Mask, Rotation) with examples. §5 Patterns — "Role of presets" block added explaining Base Patterns. §5.1 Scale Short — description expanded, Avatar example added. A possible naming discrepancy between slow-ease-out vs standard-ease-in-out curves was identified — requires verification against Figma. |
| 1.4.0 | 2026-04-23 | Eugene Beloussov | Sections added from Figma documentation: §0.4 "Recommendations: when to use animation" (when we use / do not use, overscroll/overdrag); §1.0 "Role of primitives"; §2.5 "How to choose a curve" (scenario→curve table); §3.0 "How to choose a duration" (range→type→examples table). |
| 1.3.0 | 2026-04-23 | Eugene Beloussov | "Ready-made component animations" section added (section 6). Three types of press animation: `pushHighlight` (linear + duration.short2, no scale), `pushItem` (scale 100→95%, standard-ease-in-out + duration.short3), `pushButton` (combined scale and color, same). Three types of appearance animation: `scaleS` (scale 0→100%, standard-ease-in-out + duration.short3), `fadeM` (opacity + scale 50→100%, standard-ease-in-out + duration.medium1), `moveM` (slow-ease-out/accelerated-ease-in + duration.long1). CSS Custom Properties supplemented with `--component-*` block. Reduced-motion overrides all component animations to 0ms. Section numbering updated: was 6–12, now 7–13 + new §6. |
| 1.2.0 | 2026-04-23 | Eugene Beloussov | Duration primitives renamed to named tokens by type: `duration.short1–3` (100–200ms), `duration.medium1–2` (250–300ms), `duration.long1–3` (400–600ms), `duration.custom`. Short/Medium/Long type descriptions with usage examples. Transforming section expanded with "Base Parameters" section: Color, Scale, Position, Opacity, Mask, Rotation. Duration references in Transforming tables updated to named tokens. 8 precise Pattern presets added with appear/hide specification: Scale Short, Position, Position Short, Mask, Scale and Opacity, Color, Scale Medium, Position Medium. CSS Custom Properties expanded: duration variables renamed, 15 pattern-appear/hide variables added. Reduced-motion section supplemented with all new patterns. |

> New updates are formatted as a separate `Update X.Y` frame in the components file, then transferred to this document.

---

## Generated tokens — `tokens/generated/motion.css`

```css
/* ================================================================
 * Oymyakon Design System — Motion Tokens (motion.css)
 * AUTO-GENERATED by build-dtcg.js — DO NOT EDIT MANUALLY
 * Source: tokens/src/motion/*.json
 * ================================================================ */

:root {

  /* ── Curves primitives — consumed only through a preset below ── */
  /* Starts slow, ends fast. Exits and dismissals — the element is leaving, so it accelerates away. */
  --curve-accelerated-ease-in: cubic-bezier(0.5, 0, 0.75, 0);
  /* The default. Symmetric ease for presses, enters and scale changes. */
  --curve-standard-ease-in-out: cubic-bezier(0.25, 1, 0.5, 1);
  /* Starts fast, settles slowly. State changes and position moves. */
  --curve-slow-ease-out: cubic-bezier(0.4, 0, 0.2, 1);
  /* Constant rate. Loops and colour-only changes, where acceleration would read as a stutter. */
  --curve-linear: cubic-bezier(0.25, 0.25, 0.75, 0.75);

  /* ── Duration primitives ── */
  --duration-short1: 100ms;
  --duration-short2: 150ms;
  --duration-short3: 200ms;
  --duration-medium1: 250ms;
  --duration-medium2: 300ms;
  --duration-long1: 400ms;
  --duration-long2: 500ms;
  --duration-long3: 600ms;

  /* ── Transforming presets — state, enter, exit ── */
  /* Transforming/Instant — the moment of contact. */
  --transforming-pressed: 100ms cubic-bezier(0.5, 0, 0.75, 0);
  /* Transforming/Instant — contact ends. */
  --transforming-released: 150ms cubic-bezier(0.25, 1, 0.5, 1);
  /* Transforming/State — a state change that must feel immediate. */
  --transforming-state-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1);
  /* Transforming/State — the default state change: colour, border, shadow. */
  --transforming-state-default: 200ms cubic-bezier(0.4, 0, 0.2, 1);
  /* Transforming/State — a state change on a large surface. */
  --transforming-state-slow: 300ms cubic-bezier(0.4, 0, 0.2, 1);
  /* Transforming/Enter — a small element appears. */
  --transforming-enter-fast: 150ms cubic-bezier(0.25, 1, 0.5, 1);
  /* Transforming/Enter — the default appearance. */
  --transforming-enter-default: 250ms cubic-bezier(0.25, 1, 0.5, 1);
  /* Transforming/Enter — a large overlay appears. */
  --transforming-enter-slow: 400ms cubic-bezier(0.25, 1, 0.5, 1);
  /* Transforming/Exit — a small element leaves. */
  --transforming-exit-fast: 100ms cubic-bezier(0.5, 0, 0.75, 0);
  /* Transforming/Exit — the default dismissal. */
  --transforming-exit-default: 200ms cubic-bezier(0.5, 0, 0.75, 0);
  /* Transforming/Exit — a large overlay leaves. */
  --transforming-exit-slow: 300ms cubic-bezier(0.5, 0, 0.75, 0);

  /* ── Pattern presets — appear / hide ── */
  /* Patterns/Scale Short — appear. */
  --pattern-scale-short-appear: 100ms cubic-bezier(0.25, 1, 0.5, 1);
  /* Patterns/Scale Short — hide. */
  --pattern-scale-short-hide: 200ms cubic-bezier(0.25, 1, 0.5, 1);
  /* Patterns/Position — appear. */
  --pattern-position-appear: 400ms cubic-bezier(0.4, 0, 0.2, 1);
  /* Patterns/Position — disappear. */
  --pattern-position-disappear: 400ms cubic-bezier(0.5, 0, 0.75, 0);
  /* Patterns/Position Short — appear. */
  --pattern-position-short-appear: 150ms cubic-bezier(0.4, 0, 0.2, 1);
  /* Patterns/Position Short — hide. */
  --pattern-position-short-hide: 150ms cubic-bezier(0.5, 0, 0.75, 0);
  /* Patterns/Mask — appear. */
  --pattern-mask-appear: 250ms cubic-bezier(0.4, 0, 0.2, 1);
  /* Patterns/Mask — hide. */
  --pattern-mask-hide: 250ms cubic-bezier(0.5, 0, 0.75, 0);
  /* Patterns/Scale and Opacity — appear. */
  --pattern-scale-opacity-appear: 250ms cubic-bezier(0.25, 1, 0.5, 1);
  /* Patterns/Scale and Opacity — hide. */
  --pattern-scale-opacity-hide: 250ms cubic-bezier(0.25, 1, 0.5, 1);
  /* Patterns/Color — a colour-only change, linear so it reads as even. */
  --pattern-color: 200ms cubic-bezier(0.25, 0.25, 0.75, 0.75);
  /* Patterns/Scale Medium — appear. */
  --pattern-scale-medium-appear: 400ms cubic-bezier(0.25, 1, 0.5, 1);
  /* Patterns/Scale Medium — hide. */
  --pattern-scale-medium-hide: 400ms cubic-bezier(0.25, 1, 0.5, 1);
  /* Patterns/Position Medium — appear. */
  --pattern-position-medium-appear: 400ms cubic-bezier(0.25, 1, 0.5, 1);
  /* Patterns/Position Medium — hide. */
  --pattern-position-medium-hide: 400ms cubic-bezier(0.25, 1, 0.5, 1);

  /* ── Component recipes — §6 ── */
  /* pushHighlight — background only, no scale. Cell, list rows. */
  --component-push-highlight: 150ms cubic-bezier(0.25, 0.25, 0.75, 0.75);
  /* pushItem — scale 100% → 95%. Rating slots, cards. */
  --component-push-item-press: 200ms cubic-bezier(0.25, 1, 0.5, 1);
  /* pushItem — scale 95% → 100%. */
  --component-push-item-release: 200ms cubic-bezier(0.25, 1, 0.5, 1);
  /* pushButton — scale and fill together, 100% → 95%. Button, all variants. */
  --component-push-button-press: 200ms cubic-bezier(0.25, 1, 0.5, 1);
  /* pushButton — scale and fill together, 95% → 100%. */
  --component-push-button-release: 200ms cubic-bezier(0.25, 1, 0.5, 1);
  /* scaleS — a small element scales in from 0. */
  --component-scale-s-appear: 200ms cubic-bezier(0.25, 1, 0.5, 1);
  /* scaleS — scales back out. */
  --component-scale-s-hide: 200ms cubic-bezier(0.25, 1, 0.5, 1);
  /* fadeM — opacity with a 50% → 100% scale. */
  --component-fade-m-appear: 250ms cubic-bezier(0.25, 1, 0.5, 1);
  /* fadeM — hide. */
  --component-fade-m-hide: 250ms cubic-bezier(0.25, 1, 0.5, 1);
  /* moveM — a large surface moves in. */
  --component-move-m-appear: 400ms cubic-bezier(0.4, 0, 0.2, 1);
  /* moveM — moves out. */
  --component-move-m-hide: 400ms cubic-bezier(0.5, 0, 0.75, 0);
  /* bounceWave — per-slot stagger. A duration on its own: the curves are fixed inside the recipe (§6.3). */
  --component-bounce-wave-step: 70ms;
  /* bounceWave — one slot's scale 1 → 1.28 → 1, peak at 110ms. */
  --component-bounce-wave-bounce: 380ms;
  /* bounceWave — the clip-path reveal, fired at the peak. */
  --component-bounce-wave-fill: 160ms;

  /* ── Loop patterns — animation shorthands, keyframes below ── */
  /* Patterns/Shimmer — the Skeleton sweep. Tied to Skeleton/Wave in color-rules.md. */
  --pattern-shimmer: shimmer 1200ms cubic-bezier(0.25, 0.25, 0.75, 0.75) infinite;
  /* Patterns/Pulse — badge indicators, online statuses, notification dots. */
  --pattern-pulse: pulse 1500ms cubic-bezier(0.4, 0, 0.2, 1) infinite alternate;
  /* Patterns/Spin — loading indicators. Consumed by Loader, and through it by Button's Loading state. */
  --pattern-spin: spin 800ms cubic-bezier(0.25, 0.25, 0.75, 0.75) infinite;
  /* Patterns/FadeIn — overlays and backdrops. */
  --pattern-fade-in: fadeIn 200ms cubic-bezier(0.25, 1, 0.5, 1) both;
  /* Patterns/FadeOut — overlays and backdrops. */
  --pattern-fade-out: fadeOut 150ms cubic-bezier(0.5, 0, 0.75, 0) both;
}

/* ── Keyframes named by the loop patterns above ── */
@keyframes shimmer {
  0% { transform: translateX(-100%); }
  100% { transform: translateX(100%); }
}

@keyframes pulse {
  0% { opacity: 1; transform: scale(1); }
  100% { opacity: 0.4; transform: scale(0.88); }
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

@keyframes fadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes fadeOut {
  from { opacity: 1; }
  to { opacity: 0; }
}

/* ── Reduced motion — every preset to zero, every loop to none.
      A component never needs its own override: zeroing happens at the token,
      which is the whole point of naming the motion instead of inlining it. ── */
@media (prefers-reduced-motion: reduce) {
  :root {
    --transforming-pressed: 0ms linear;
    --transforming-released: 0ms linear;
    --transforming-state-fast: 0ms linear;
    --transforming-state-default: 0ms linear;
    --transforming-state-slow: 0ms linear;
    --transforming-enter-fast: 0ms linear;
    --transforming-enter-default: 0ms linear;
    --transforming-enter-slow: 0ms linear;
    --transforming-exit-fast: 0ms linear;
    --transforming-exit-default: 0ms linear;
    --transforming-exit-slow: 0ms linear;
    --pattern-scale-short-appear: 0ms linear;
    --pattern-scale-short-hide: 0ms linear;
    --pattern-position-appear: 0ms linear;
    --pattern-position-disappear: 0ms linear;
    --pattern-position-short-appear: 0ms linear;
    --pattern-position-short-hide: 0ms linear;
    --pattern-mask-appear: 0ms linear;
    --pattern-mask-hide: 0ms linear;
    --pattern-scale-opacity-appear: 0ms linear;
    --pattern-scale-opacity-hide: 0ms linear;
    --pattern-color: 0ms linear;
    --pattern-scale-medium-appear: 0ms linear;
    --pattern-scale-medium-hide: 0ms linear;
    --pattern-position-medium-appear: 0ms linear;
    --pattern-position-medium-hide: 0ms linear;
    --component-push-highlight: 0ms linear;
    --component-push-item-press: 0ms linear;
    --component-push-item-release: 0ms linear;
    --component-push-button-press: 0ms linear;
    --component-push-button-release: 0ms linear;
    --component-scale-s-appear: 0ms linear;
    --component-scale-s-hide: 0ms linear;
    --component-fade-m-appear: 0ms linear;
    --component-fade-m-hide: 0ms linear;
    --component-move-m-appear: 0ms linear;
    --component-move-m-hide: 0ms linear;
    --component-bounce-wave-step: 0ms;
    --component-bounce-wave-bounce: 0ms;
    --component-bounce-wave-fill: 0ms;
    --pattern-shimmer: none;
    --pattern-pulse: none;
    --pattern-spin: none;
    --pattern-fade-in: none;
    --pattern-fade-out: none;
  }
}
```
