---
title: 'Lists & Feedback'
excerpt: 'List add, error shake, and when CSS press beats GSAP.'
icon: 'MousePointer2'
---

## Feedback that earns its duration

List mutations and validation need short, purposeful motion — enough to show what changed, not enough to slow the task. Prefer transforms and opacity; reach for height only when the layout must open space for a new row.

## Add list item

When a row mounts, animate it from collapsed / invisible so the list does not pop. `height: 0` → auto height (GSAP can tween height) plus fade makes the insert readable.

Click Add: the new item grows into place instead of appearing fully formed.

<ComponentPreview>
  <GsapListAddRemove />

  <DocsFileTabs title="List add">

```tsx title="list-add.tsx"
gsap.from('.list-item:last-child', {
  height: 0,
  autoAlpha: 0,
  duration: 0.3,
  ease: 'power2.out',
});
```

```html title="list-add-markup.html"
<div class="space-y-2">
  <div class="gsap-list-item rounded-lg border bg-card px-3 py-2">Design review</div>
  <div class="gsap-list-item rounded-lg border bg-card px-3 py-2">API spec</div>
  <button>Add item</button>
</div>
```

  </DocsFileTabs>
</ComponentPreview>

## Error shake (rare)

A horizontal shake is **attention**, not decoration. Use it for failed validation once — not on every keystroke. Short `repeat` + `yoyo` on `x` is enough; long shakes feel cartoonish.

Submit with invalid input: the field jitters, then rests. Do not loop forever.

<ComponentPreview>
  <GsapErrorShake />

  <DocsFileTabs title="Error shake">

```tsx title="error-shake.tsx"
gsap.fromTo('.field', { x: 0 }, { x: 8, duration: 0.08, repeat: 5, yoyo: true, ease: 'power1.inOut' });
```

```html title="error-shake-markup.html"
<input
  class="gsap-shake-field w-full rounded-md border px-3 py-2"
  value="not-an-email"
  placeholder="Invalid email"
/>
<button>Submit (error)</button>
```

  </DocsFileTabs>
</ComponentPreview>

## CSS press only

Press feedback (scale on `:active`) runs dozens of times a day. CSS transitions at **100–160ms** beat GSAP here — no JS on every pointer down, and the browser already composites `transform`.

Press and hold: the button scales slightly. No tween instance, no cleanup.

<ComponentPreview>
  <GsapCssPressOnly />

  <DocsFileTabs title="Css press">

```tsx title="css-press.tsx"
/* 100–160ms ease-out — no GSAP */
button:active { transform: scale(0.97); }
```

```html title="css-press-markup.html"
<button class="demo-press-btn">Save changes</button>
```

  </DocsFileTabs>
</ComponentPreview>

For layout reflow between filter states, see [Flip](/learn/gsap-react/plugins/flip-drag).
