# How to Use @starting-style for CSS-Only Entry Animations

> Learn to create CSS-only entry animations using @starting-style. Define an element's initial state for smooth transitions without JavaScript.

- Repository: [Emil Kowalski/skills](https://github.com/emilkowalski/skills)
- Tags: how-to-guide
- Published: 2026-08-04

---

**The `@starting-style` CSS at-rule defines an element's initial visual state before the first paint, allowing the browser to animate from that starting state to the final state without JavaScript.**

The `emilkowalski/skills` repository promotes `@starting-style` as the preferred method for static entry animations in modern web applications. According to the animation standards documented in [`skills/review-animations/STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/STANDARDS.md), this approach eliminates the need for `useEffect` hooks or `data-mounted` attributes while keeping motion definitions in CSS where they belong.

## What Is @starting-style?

`@starting-style` is a CSS at-rule that establishes the initial property values of an element *before* the browser renders the first frame. When the page loads, the element is painted using the styles inside the `@starting-style` block; immediately after the first paint, the normal styles take over and the transition animates to the final state.

As implemented in `emilkowalski/skills`, this technique is particularly effective for **toast notifications**, **modal dialogs**, and **list items** that need to slide or fade into view. The rule is documented at line 89 of [`skills/review-animations/STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/STANDARDS.md) and referenced throughout the skill guides including [`skills/review-animations/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/SKILL.md) at line 73.

## Benefits of CSS-Only Entry Animations

Using `@starting-style` for enter animations provides specific architectural advantages over JavaScript-driven approaches:

- **Zero-JS overhead** – The browser computes the start state during layout, avoiding extra render passes caused by mounting logic or state synchronization.
- **GPU-friendly performance** – When combined with transitions on `opacity` and `transform`, the animation runs entirely on the compositor thread, minimizing main-thread work.
- **Instant interruptibility** – Because the animation uses CSS transitions rather than keyframe animations, new style rules can immediately override or reverse the motion.
- **Improved maintainability** – All entrance logic lives in a single `@starting-style` block rather than being split between CSS and JavaScript files.

## Implementing @starting-style in Components

### Basic Toast Entry Animation

The following pattern from [`skills/review-animations/STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/STANDARDS.md) demonstrates a standard bottom-up entrance animation:

```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 begins off-screen and invisible. Once the browser paints, the transition automatically animates the element to its final position without requiring JavaScript to trigger the animation.

### Browser Fallback Strategy

For browsers that do not support `@starting-style` (older versions of Safari and Chrome), the repository recommends the classic `data-mounted` pattern documented at line 99 of [`skills/review-animations/STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/STANDARDS.md):

```css
/* Inline fallback – also from STANDARDS.md line 99 */
.toast[data-mounted] {
  opacity: 1;
  transform: translateY(0);
}

```

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

```

This fallback achieves the same visual effect by adding a data attribute after the first render, though it requires a small amount of JavaScript.

### Respecting Reduced Motion Preferences

To maintain accessibility, wrap the animation logic in a `prefers-reduced-motion` media query. This disables the transition while ensuring the element appears instantly in its final state:

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

```

### Staggered List Entry Animations

For cascading entrance effects, apply `@starting-style` to list items with custom delay variables:

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

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

```

Assign incremental `--delay` values (e.g., `0ms`, `50ms`, `100ms`) to each item to create a staggered animation sequence entirely in CSS.

## Key Source Files

The implementation patterns above are derived from specific files in the `emilkowalski/skills` repository:

- **[`skills/review-animations/STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/STANDARDS.md)** – Core documentation of `@starting-style` usage, example implementations, and fallback strategies.
- **[`skills/review-animations/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/SKILL.md)** – High-level guide referencing the rule and explaining when to prefer CSS transitions over JavaScript.
- **[`skills/improve-animations/AUDIT.md`](https://github.com/emilkowalski/skills/blob/main/skills/improve-animations/AUDIT.md)** – Auditing checklist that identifies `@starting-style` as the "entry without JS" pattern.
- **[`skills/emil-design-eng/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/skills/emil-design-eng/SKILL.md)** – Contains the section "Animate enter states with @starting-style" providing concrete component examples.

## Summary

- **`@starting-style`** defines initial property values before the first paint, enabling CSS-only entry animations.
- **Zero JavaScript** is required for the animation to trigger, reducing boilerplate and improving performance.
- **GPU acceleration** is achieved by animating only `transform` and `opacity` properties.
- **Graceful degradation** is available via the `data-mounted` attribute pattern for unsupported browsers.
- **Accessibility** is maintained by wrapping animations in `prefers-reduced-motion` media queries.

## Frequently Asked Questions

### What browsers support @starting-style?

`@starting-style` is supported in modern versions of Chrome and Edge, with Safari support added in recent releases. For older browsers, the `emilkowalski/skills` repository recommends implementing a fallback using the `data-mounted` attribute pattern as documented in [`STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/STANDARDS.md) at line 99.

### How does @starting-style differ from CSS keyframe animations?

Unlike `@keyframes`, which define explicit animation steps, `@starting-style` establishes the initial state for a CSS transition. The browser automatically interpolates between the starting styles and the element's normal styles, making it ideal for entrance animations that should respond dynamically to style changes.

### Can I use @starting-style with React or other frameworks?

Yes, `@starting-style` works with any framework or vanilla HTML because it is a pure CSS feature. You do not need `useEffect`, `useLayoutEffect`, or `componentDidMount` to trigger the animation, though you may still need JavaScript for the fallback pattern in unsupported browsers.

### How do I prevent entry animations for users who prefer reduced motion?

Wrap the `@starting-style` rule and transition properties in a `@media (prefers-reduced-motion: reduce)` query. Set the starting styles to match the final state and remove the transition, ensuring the element appears instantly without motion while maintaining layout stability.