# Using @starting-style for CSS-Only Enter Animations: A Zero-JavaScript Approach

> Master CSS-only enter animations with @starting-style. Define initial states for smooth, zero-JavaScript transitions directly in CSS for faster web performance.

- Repository: [Emil Kowalski/skills](https://github.com/emilkowalski/skills)
- Tags: tutorial
- Published: 2026-07-29

---

**The `@starting-style` CSS at-rule lets you define an element's initial visual state before the first paint, enabling smooth entry animations through pure CSS transitions without JavaScript mounting logic.**

The `emilkowalski/skills` repository documents a robust pattern for creating performant UI entrance effects using `@starting-style`. This technique, detailed in [`skills/review-animations/STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/STANDARDS.md), eliminates the need for React `useEffect` hooks or `data-mounted` attributes to trigger initial animations, keeping motion definitions in stylesheets where they belong.

## What Is @starting-style?

`@starting-style` is a CSS at-rule that establishes the initial computed values for an element's properties prior to the browser rendering the first frame. When the page loads, the element initially appears with the styles defined inside the `@starting-style` block; immediately after the first paint, the element transitions to its normal declared styles.

This mechanism allows the browser to interpolate between the starting state and the final state automatically. According to [`skills/review-animations/STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/STANDARDS.md), this is the preferred method for *static* entry animations because the browser computes the start state during layout, avoiding extra render passes caused by JavaScript mounting logic.

## Key Benefits of CSS-Only Entry Animations

- **Zero-JavaScript overhead**: The browser handles the animation lifecycle internally, removing the need for `useEffect` hooks to set mounting states.
- **GPU optimization**: When combined with `transform` and `opacity` properties, the animation runs entirely on the compositor thread, minimizing main-thread work.
- **Interruptibility**: Because the animation uses CSS transitions rather than keyframes, it can be overridden instantly by new style rules or user interactions.
- **Maintainability**: All entrance logic lives within CSS, reducing component boilerplate and centralizing motion definitions.

## Implementation Examples from the Source

### Basic Toast Entry Animation

This snippet from [`skills/review-animations/STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/STANDARDS.md) demonstrates a toast notification sliding up while fading in:

```css
/* File: skills/review-animations/STANDARDS.md */
.toast {
  /* Final state after entry */
  opacity: 1;
  transform: translateY(0);
  transition: opacity 400ms ease, transform 400ms ease;

  /* Initial state before the first paint */
  @starting-style {
    opacity: 0;
    transform: translateY(100%);
  }
}

```

The toast starts off-screen and invisible. Once the browser completes the initial paint, the `transition` properties automatically animate the element to its final position.

### Staggered List Items

For cascading entrance effects, apply a custom delay property to each item while maintaining the CSS-only approach:

```css
.list-item {
  opacity: 1;
  transform: translateY(0);
  transition: opacity 300ms ease, transform 300ms ease;
  transition-delay: var(--delay);

  @starting-style {
    opacity: 0;
    transform: translateY(20px);
  }
}

```

Each item receives an inline `--delay` value (e.g., `0ms`, `50ms`, `100ms`) to create a staggered reveal without JavaScript timers.

### Respecting Reduced Motion Preferences

Accessibility requires accommodating users who prefer minimal animation. Wrap the transition in a media query to disable motion while preserving the final state:

```css
@media (prefers-reduced-motion: reduce) {
  .toast {
    transition: none;
    @starting-style {
      opacity: 1;
      transform: translateY(0);
    }
  }
}

```

This ensures the element appears instantly in its final position when `prefers-reduced-motion` is active.

## Fallback Strategy for Legacy Browsers

When `@starting-style` is unavailable (older versions of Safari or Chrome), the repository recommends a graceful degradation pattern documented at line 99 of [`skills/review-animations/STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/STANDARDS.md). Use the classic `data-mounted` attribute approach:

```css
/* Fallback from STANDARDS.md */
.toast[data-mounted] {
  opacity: 1;
  transform: translateY(0);
}

```

```javascript
// In a React component
useEffect(() => {
  const el = ref.current;
  if (el) el.dataset.mounted = '';
}, []);

```

The JavaScript sets the attribute after the initial render, triggering the CSS transition in browsers that lack native `@starting-style` support.

## Summary

- `@starting-style` defines initial property values before the first paint, enabling CSS-only entry animations.
- The technique is documented in [`skills/review-animations/STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/STANDARDS.md) and promoted as the standard for static entry effects in the `emilkowalski/skills` repository.
- **GPU-friendly animations** result from transitioning only `transform` and `opacity`.
- A **JavaScript fallback** using `data-mounted` attributes supports legacy browsers while the primary implementation requires zero JavaScript.
- Always wrap animations in **`prefers-reduced-motion`** media queries to maintain accessibility.

## Frequently Asked Questions

### What browsers support @starting-style?

`@starting-style` is supported in Chrome and Edge versions 117+, Opera 103+, and Safari 17.5+. For older browsers, implement the `data-mounted` fallback pattern described in [`skills/review-animations/STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/STANDARDS.md) line 99.

### How does @starting-style differ from the data-mounted pattern?

`@starting-style` is declarative CSS that requires no JavaScript execution to set the initial state, reducing render passes and component complexity. The `data-mounted` pattern relies on `useEffect` or similar lifecycle hooks to add an attribute after mounting, which can cause layout thrashing if not handled carefully.

### Can I use @starting-style for exit animations?

No. `@starting-style` specifically handles the entry state before the first paint. For exit animations, use the CSS `:exit` selector with `transition` properties or the `data-mounted` removal technique, as exit states require the element to exist in the DOM before animating out.

### Is @starting-style suitable for complex keyframe animations?

While technically possible, the repository recommends `@starting-style` primarily for simple transitions on `opacity` and `transform`. Complex entrance sequences with multiple steps should use `@keyframes` with `animation-fill-mode: forwards`, whereas `@starting-style` excels at single-transition entry effects like fades and slides.