Skip to content
Back to skills

Design Engineering

ASecurity

UI craftsmanship: animation rules, easing, micro-interactions, state polish. Triggers: animation, transition, ease-out, motion, micro-interaction, hover, loading state, UI polish.

  • 177 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added May 27, 2026
developmentjavascriptgojavaangularapifrontendperformance

Works with

  • cli
  • api

Security analysis

A100/100

Scanned May 27, 2026

npx -y skills add softspark/ai-toolkit --skill design-engineering --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Design Engineering?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Design Engineering
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/softspark-design-engineering/badge)](https://www.skillsdirectory.com/skills/softspark-design-engineering)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: design-engineering
description: "UI craftsmanship: animation rules, easing, micro-interactions, state polish. Triggers: animation, transition, ease-out, motion, micro-interaction, hover, loading state, UI polish."
effort: medium
user-invocable: false
allowed-tools: Read
---

# Design Engineering Skill

Based on Emil Kowalski's design engineering philosophy — UI polish, component craftsmanship, and the compound value of invisible details.

## Core Principles

- **Taste is trainable.** Develops through studying exceptional work, reverse-engineering animations, and intentional practice.
- **Invisible details create love.** Most UI refinements users never consciously register — but combined they produce something stunning.
- **Beauty differentiates.** When functionality is table stakes, aesthetic excellence becomes genuine leverage.

## Animation Decision Framework

### Frequency determines approach

| Usage Pattern | Strategy |
|---|---|
| 100+ daily | No animation |
| Tens daily | Drastically reduce |
| Occasional | Standard animation |
| Rare/first-time | Add delight |

**Never animate keyboard-initiated actions** — they repeat hundreds of times daily, making animation feel sluggish.

### Purpose validation

Every animation requires justification: spatial consistency, state indication, explanation, user feedback, or preventing jarring transitions. "It looks cool" alone disqualifies frequent interactions.

### Easing rules

| Direction | Easing | Why |
|---|---|---|
| Entering elements | `ease-out` | Immediate feedback |
| On-screen movement | `ease-in-out` | Natural acceleration |
| Hover/color changes | `ease` | Smooth transition |
| Constant motion | `linear` | No acceleration |

**Critical:** Abandon default CSS easings. Use custom curves:

```css
/* Punchy entrance */
transition-timing-function: cubic-bezier(0.23, 1, 0.32, 1);
```

**Never use `ease-in`** — it delays initial movement exactly when attention peaks, making interfaces feel sluggish.

### Duration guidelines

| Element | Timing |
|---|---|
| Button press | 100-160ms |
| Tooltips, small popovers | 125-200ms |
| Dropdowns, selects | 150-250ms |
| Modals, drawers | 200-500ms |

UI animations should stay **under 300ms**. Speed perception matters as much as actual speed.

## Component Patterns

### Buttons must respond

```css
button:active {
  transform: scale(0.97);
}
```

Tactile feedback confirming interface responsiveness.

### Never scale from zero

```css
/* Bad */
.enter { transform: scale(0); opacity: 0; }

/* Good — natural entrance */
.enter { transform: scale(0.95); opacity: 0; }
```

Real-world objects don't vanish and reappear. Start from `scale(0.95)`.

### Popovers scale from triggers

```css
.popover {
  transform-origin: var(--radix-popover-content-transform-origin);
}
```

Exception: modals keep centered origin (viewport-anchored, not trigger-anchored).

### Tooltip optimization

Initial tooltip includes delay; subsequent hovers skip both delay and animation via `[data-instant]` attribute — perceived speed without defeating accidental activation prevention.

## Transform Mastery

### Percentage translations

```css
/* Moves by own height — perfect for toasts, drawers */
transform: translateY(100%);
```

No hardcoded pixel values needed.

### Scale affects children

Unlike `width`/`height`, `scale()` proportionally scales content, icons, and text. Intentional feature, not a bug.

### 3D transforms

```css
.orbit {
  transform-style: preserve-3d;
}
```

Enables orbit animations and coin flips without JavaScript.

## Clip-path Animation

`clip-path: inset(top right bottom left)` creates rectangular clipping regions:

### Tab color transitions

Stack tab lists, clip the active copy, animate clip-path on change for seamless color shifting.

### Hold-to-delete

```css
.delete-overlay {
  clip-path: inset(0 100% 0 0);
  transition: clip-path 200ms ease-out; /* fast snap-back on release */
}
.delete-button:active .delete-overlay {
  clip-path: inset(0 0 0 0);
  transition: clip-path 2s linear; /* slow fill while holding */
}
```

### Image reveals

```css
.reveal {
  clip-path: inset(0 0 100% 0); /* hidden */
}
.reveal.visible {
  clip-path: inset(0 0 0 0); /* revealed */
}
```

### Comparison sliders

Overlay images, clip top one by adjusting right inset based on drag position.

## Performance Rules

### GPU acceleration

Only animate `transform` and `opacity` — these skip layout and paint. Animating `padding`, `margin`, `height`, `width` triggers full rendering pipeline.

### CSS variables caveat

Changing parent CSS variables recalculates styles on all children. Update `transform` directly on elements instead.

### Framer Motion gotcha

Shorthand properties (`x`, `y`, `scale`) use main-thread `requestAnimationFrame`, not GPU:

```tsx
// Bad — main thread
<motion.div animate={{ x: 100 }} />

// Good — GPU accelerated
<motion.div animate={{ transform: "translateX(100px)" }} />
```

### CSS beats JavaScript

CSS animations run off-thread and remain smooth during page loads. Framer Motion drops frames when browser is busy. Use CSS for predetermined animations; JavaScript for dynamic, interruptible ones.

## Accessibility

### Reduced motion

Keep opacity and color transitions (aid comprehension). Remove movement and position animations:

```css
@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}
```

### Touch hover protection

```css
@media (hover: hover) and (pointer: fine) {
  .card:hover {
    transform: translateY(-2px);
  }
}
```

Touch triggers false hover positives — always gate hover animations.

## Building Loved Components

Five principles (from Sonner, 13M+ weekly downloads):

1. **Developer experience first** — minimal setup friction, insert once, use globally
2. **Excellent defaults** — ship beautifully configured out-of-box
3. **Identity through naming** — a memorable name resonates
4. **Invisible edge cases** — pause timers when hidden, handle pointer capture during drag
5. **Transitions over keyframes** — rapid additions cause keyframe restart from zero; transitions retarget smoothly

### Cohesion

Animation personality should match component identity. Playful components can bounce; professional dashboards stay crisp.

### Asymmetric timing

Deliberate actions stay slow (2s linear for hold-to-delete), system responses snap fast (200ms ease-out for release).

## Review Checklist

| Issue | Resolution |
|---|---|
| `transition: all` | Specify properties: `transition: transform 200ms ease-out` |
| `scale(0)` entries | Start `scale(0.95)` with `opacity: 0` |
| `ease-in` on UI | Switch to `ease-out` or custom curve |
| Popover `transform-origin: center` | Use trigger-aware CSS variable |
| Animation on keyboard actions | Remove entirely |
| Duration > 300ms UI | Reduce to 150-250ms |
| Hover without media query | Add `@media (hover: hover) and (pointer: fine)` |
| Keyframes on rapid triggers | Use CSS transitions |
| Framer `x`/`y` under load | Use `transform: "translateX()"` |
| Identical enter/exit speed | Make exit faster (e.g., 2s enter, 200ms exit) |
| Simultaneous element appearance | Stagger 30-80ms between items |

## Anti-Patterns

- `transition: all` — animates unintended properties, hurts performance
- `ease-in` on UI elements — delays feedback when attention peaks
- Animating `height`/`width`/`margin` — triggers layout recalculation
- Same duration for enter and exit — exits should be faster
- Hover effects without `@media (hover: hover)` — breaks touch devices
- Framer Motion shorthands under load — drops frames on main thread

## Rules

- **MUST** specify exact properties in `transition` (`transition: transform 200ms ease-out`) — never `transition: all`
- **MUST** use `ease-out` (or a custom curve) on UI appearances; `ease-in` delays feedback at the moment the user's attention peaks
- **NEVER** animate `height`, `width`, `margin`, or `top/left` — animate `transform` and `opacity` only. Layout-triggering properties drop frames under load.
- **NEVER** add motion for decorative reasons alone — every animation must serve meaning (status change, spatial relationship, progress)
- **CRITICAL**: exit is faster than enter. A 2s linear enter (hold-to-delete) needs a 200ms ease-out exit. Symmetrical durations feel sluggish.
- **MANDATORY**: any animation longer than 300ms for UI feedback needs an explicit justification — the user perceives >300ms as "laggy", not "smooth"

## Gotchas

- `transform: translateX(-50%)` on an element that will animate `opacity` triggers a paint on every frame because the browser cannot composite the layer. Add `will-change: transform, opacity` to hint the compositor — but only during the animation, not permanently (it consumes GPU memory).
- Framer Motion's `x={100}` prop is a shortcut for `transform: translateX(100px)`, but under load it falls back to the main thread. Use the longhand `style={{ transform: "translateX(100px)" }}` for guaranteed compositor path.
- `@media (prefers-reduced-motion: reduce)` is widely supported but often forgotten. Users with vestibular disorders or pointer-device sensitivity will notice; include a reduced-motion override for every non-trivial animation.
- Chrome's Performance tab samples animations, but the sampling rate is 1kHz — sub-millisecond jank is invisible. For micro-animations, prefer `performance.mark` and `measure` with explicit timestamps.
- CSS keyframe animations re-trigger on every class toggle. On rapidly-updating state (drag, hover), transitions are cheaper and smoother; keyframes are for one-shot entries/exits.

## When NOT to Load

- For **accessibility** beyond motion (contrast, focus, ARIA) — use `/a11y-validate`
- For component-library architecture (Radix, Headless UI, ShadCN) — use `/frontend-specialist` agent
- For **information architecture** and user flows — use `/ux-designer` agent
- For generic CSS patterns without motion — this skill is motion-specific
- For **brand voice** / content tone — use `/brand-voice`

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…