# Using @starting-style for CSS Entry Animations Without JavaScript

> Master CSS entry animations with @starting-style. Achieve dynamic entrance effects without JavaScript and simplify your frontend development.

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

---

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

The `@starting-style` at-rule lets you create smooth, performant entry animations entirely in CSS. According to the emilkowalski/skills repository (the Instagit skillset), this technique eliminates the need for React `useEffect` hooks or `data-mounted` attributes while keeping animations on the GPU compositor thread.

## What @starting-style Does and How It Works

`@starting-style` declares properties that apply **only** during the first paint cycle. After that frame renders, the element reverts to its normal styles, triggering CSS transitions to animate between the two states.

This architecture provides three key advantages:

- **Zero JavaScript overhead** — the browser computes start states during layout, avoiding extra render passes from mounting logic
- **GPU acceleration** — when combined with `opacity` and `transform` transitions, animations run entirely on the compositor thread
- **Instant reversibility** — CSS transitions can be overridden immediately by new style rules, unlike keyframe animations

The technique is documented in [[`skills/review-animations/STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/STANDARDS.md)](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/STANDARDS.md#L89) and promoted throughout the animation guides as the preferred approach for static entry animations.

## Basic Toast Entry Animation Example

Here's the canonical pattern from [`STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/STANDARDS.md):

```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 invisible and off-screen (`translateY(100%)`). Once painting completes, the transition automatically animates to the final visible position. No state management, no refs, no effects required.

## Browser Fallback Strategy

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

```css
/* Fallback when @starting-style isn't supported */
.toast[data-mounted] {
  opacity: 1;
  transform: translateY(0);
}

```

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

```

The JavaScript fallback applies the same visual result but requires an extra render pass. Feature detection via `@supports (@starting-style {})` lets you serve the optimal path to each browser.

## Respecting Reduced Motion Preferences

Accessibility considerations appear throughout the skillset documentation. Wrap `@starting-style` in a media query to honor user preferences:

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

```

This disables the transition while ensuring the element renders immediately in its final state—no disorienting animation, no broken layout.

## Staggered List Entry Pattern

For cascading entrances, combine `@starting-style` with CSS custom properties:

```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);
  }
}

```

Set `--delay` per item (e.g., `calc(var(--index) * 50ms)`) to create choreographed reveals without JavaScript timers or batch updates.

## Performance and Architectural Benefits

| Concern | `@starting-style` Approach | Traditional JS Approach |
|---------|---------------------------|------------------------|
| **Render passes** | Single layout + paint | Multiple React renders |
| **Thread usage** | Compositor-only with `transform`/`opacity` | Main thread for state updates |
| **Bundle impact** | Zero bytes of JS | Hooks, refs, and effect logic |
| **Cancellation** | Instant via new style rules | Requires cleanup functions |

The [[`skills/review-animations/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/SKILL.md)](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/SKILL.md#L73) file explicitly recommends CSS transitions over JavaScript for static entry animations precisely because of these characteristics.

## Common Use Cases in the Codebase

The emilkowalski/skills repository identifies these patterns as ideal candidates for `@starting-style`:

- **Toast notifications** — slide up from bottom while fading in
- **Modal dialogs** — scale from 95% with opacity fade
- **Dropdown menus** — expand from origin point
- **List items** — staggered entrance on mount

Each case shares the same requirements: a predictable initial state, a single transition target, and no need for dynamic interruption during the animation.

## Key Files and References

| File | Purpose |
|------|---------|
| [`skills/review-animations/STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/STANDARDS.md) | Core documentation with implementation examples and fallback strategy |
| [`skills/review-animations/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/skills/review-animations/SKILL.md) | High-level guidance on preferring CSS transitions for entry animations |
| [`skills/improve-animations/AUDIT.md`](https://github.com/emilkowalski/skills/blob/main/skills/improve-animations/AUDIT.md) | Checklist entry for "entry without JS" pattern |
| [`skills/emil-design-eng/SKILL.md`](https://github.com/emilkowalski/skills/blob/main/skills/emil-design-eng/SKILL.md) | Concrete component example under "Animate enter states with @starting-style" |

## Summary

- **`@starting-style`** defines pre-paint styles that enable CSS-only entrance animations
- **No JavaScript required** — eliminate `useEffect`, `useState`, and `data-mounted` patterns for static entries
- **GPU-optimized** when paired with `opacity` and `transform` transitions
- **Graceful degradation** via `data-mounted` fallback for unsupported browsers
- **Accessible by default** through `prefers-reduced-motion` media query integration
- **Best for** toast notifications, modals, dropdowns, and staggered list items

## Frequently Asked Questions

### Which browsers support @starting-style?

Chrome 116+ and Safari 17.4+ support `@starting-style`. For older versions, implement the `data-mounted` fallback documented in [`STANDARDS.md`](https://github.com/emilkowalski/skills/blob/main/STANDARDS.md) line 99. Use `@supports (@starting-style {})` for feature detection.

### Can I animate height or width with @starting-style?

Technically yes, but the repository discourages it. Animating layout properties forces main-thread recalculation and subverts the performance benefits. Stick to `opacity` and `transform` for compositor-thread animation.

### How does @starting-style differ from CSS @keyframes?

`@keyframes` define an animation sequence that plays once or loops. `@starting-style` sets a single initial state that transitions to normal styles. The latter integrates naturally with CSS transitions and requires no animation duration declaration in the keyframe itself.

### What happens if I wrap @starting-style in a media query?

The rule respects query conditions normally. In `prefers-reduced-motion: reduce`, setting the starting style equal to the final style prevents any visible animation while maintaining the same structural approach.