# How to Use @starting-style for CSS Enter Animations

> Master CSS enter animations with @starting-style. Define initial element states for seamless transitions without JavaScript. Learn this powerful technique today.

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

---

**@starting-style is a CSS at-rule that defines an element's initial visual state before the browser paints the first frame, enabling smooth entrance animations without JavaScript.**

The `emilkowalski/skills` repository documents this technique as the preferred method for handling static entry animations. According to [`skills/review-animations/STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/STANDARDS.md) (line 89) and the broader animation guides, using `@starting-style` for CSS enter animations eliminates the need for `useEffect` hooks or `data-mounted` attributes while keeping motion definitions entirely in your stylesheets.

## What is @starting-style?

`@starting-style` is a CSS at-rule that declares the initial appearance of an element *before* the browser renders the first frame. When the page loads, the element immediately adopts the properties defined inside the `@starting-style` block. After the first paint, the element's normal styles take over, and any defined `transition` animates the properties to their final state.

This mechanism makes it possible to create smooth entrance animations—such as fades, slides, or scale effects—using only CSS, without requiring JavaScript to trigger the animation or manage mount states.

## Why Use @starting-style for Enter Animations?

The `emilkowalski/skills` repository promotes `@starting-style` as the standard for entry animations because of several architectural advantages:

- **Zero-JavaScript overhead** – The browser computes the start state during layout, avoiding extra render passes caused by mounting logic.
- **GPU-friendly performance** – When combined with transitions on `opacity` and `transform`, animations run on the compositor thread, minimizing main-thread work.
- **Graceful degradation** – For browsers that don't support `@starting-style` (older Safari or Chrome versions), the repository provides a fallback pattern using the `data-mounted` attribute.
- **Interruptibility** – Because these are CSS transitions rather than keyframe animations, they can be overridden instantly by new style rules, supporting dynamic UI interactions.

## Implementation Examples from emilkowalski/skills

### Basic Toast Entry Animation

As documented at line 89 of [`skills/review-animations/STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/STANDARDS.md), the following pattern demonstrates a standard fade-and-slide entrance:

```css
.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 painted, the transition automatically animates it to the final position without JavaScript intervention.

### Fallback for Unsupported Browsers

For environments where `@starting-style` isn't available, line 99 of [`skills/review-animations/STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/STANDARDS.md) recommends the classic `data-mounted` pattern:

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

```

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

```

This approach achieves the same visual effect by triggering the transition via attribute selection after the component mounts, as referenced in [`skills/review-animations/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/SKILL.md).

### Respecting Reduced-Motion Preferences

Accessibility requires honoring user motion preferences. Wrap the animation in a media query to disable transitions while preserving the final state, a practice noted in [`skills/improve-animations/AUDIT.md`](https://github.com/emilkowalski/skills/blob/main/skills/improve-animations/AUDIT.md):

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

```

### Staggered List Entry

Create cascading entrance effects by combining `@starting-style` with custom properties, as shown in [`skills/emil-design-eng/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/skills/emil-design-eng/SKILL.md):

```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 a unique `--delay` value (e.g., `0ms`, `50ms`, `100ms`) to create the staggered effect without JavaScript timers.

## Browser Support and Fallback Strategy

While `@starting-style` is supported in modern Chromium-based browsers, older versions of Safari and Chrome lack support. The `emilkowalski/skills` repository addresses this through progressive enhancement:

1. **Primary approach**: Use `@starting-style` for capable browsers.
2. **Fallback**: Implement the `data-mounted` attribute pattern for legacy support.
3. **Detection**: The JavaScript fallback only runs when the CSS at-rule isn't recognized, ensuring optimal performance in modern browsers.

## Performance and Architectural Considerations

| Aspect | Why it matters | How @starting-style helps |
|--------|----------------|----------------------------|
| **Performance** | Animating only `transform` and `opacity` keeps work on the GPU. | Defines these properties in the initial state, then transitions them. |
| **Interruptibility** | Dynamic UI requires the ability to cancel or reverse animations. | CSS transitions can be overridden instantly by new style rules. |
| **Maintainability** | Reduces boilerplate and keeps motion in CSS. | All entrance logic lives in the `@starting-style` block. |
| **Accessibility** | Reduced-motion users need minimal movement. | Can be wrapped in `prefers-reduced-motion` media queries. |

## Summary

- **`@starting-style`** defines an element's initial state before the first paint, enabling CSS-only enter animations.
- **Zero-JavaScript** required for modern browsers, though the `data-mounted` fallback handles legacy support as documented in [`skills/review-animations/STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/STANDARDS.md).
- **GPU-optimized** when animating `opacity` and `transform`, keeping main-thread work minimal.
- **Accessible** by wrapping animations in `prefers-reduced-motion` media queries.
- **Interruptible** transitions support dynamic UI changes better than keyframe animations.

## Frequently Asked Questions

### What browsers support @starting-style?

Modern Chromium-based browsers support `@starting-style`, while older versions of Safari and Chrome may not. The `emilkowalski/skills` repository recommends using the `data-mounted` attribute fallback for these cases, ensuring animations work everywhere while optimizing for modern implementations.

### How is @starting-style different from CSS animations?

`@starting-style` works with CSS **transitions**, not `@keyframes` animations. It defines the starting values of properties before the first paint, allowing the browser to interpolate to the final state naturally. This approach is more interruptible than keyframe animations and requires no JavaScript to trigger, unlike traditional mount-based animation patterns.

### Can I use @starting-style with React?

Yes, `@starting-style` works seamlessly with React and other frameworks. Because the rule handles the initial state automatically, you can remove `useEffect` hooks that previously managed mount states. For server-side rendering or hydration scenarios, the browser handles the initial paint state without hydration mismatches, as documented in [`skills/review-animations/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/SKILL.md).

### How do I handle users who prefer reduced motion?

Wrap your `@starting-style` rules in a `@media (prefers-reduced-motion: reduce)` query. Set the transition to `none` and ensure the `@starting-style` block contains the final state values (`opacity: 1`, `transform: translateY(0)`). This ensures the element appears instantly in its final position without animation, respecting user accessibility preferences.