Project Overview
Skills (emilkowalski/skills) is a curated collection of UI interaction patterns and animation techniques built for design engineers — practitioners who own both the visual design and the frontend code. Created by Emil Kowalski, the project documents concrete animation patterns and interaction mechanics that show up repeatedly in high-quality product work.
The repo isn't a component library. You won't add it to your package.json. Instead, it's a reference — production-tested patterns you read, understand, and adapt into your own codebase. With over 16,000 stars, it's clearly useful to engineers who want their interfaces to move like native apps rather than static web pages.
If you're looking for a drop-in, versioned dependency with a changelog and semver guarantees, look elsewhere. Skills trades that for something different: unusually clean, readable implementations that you own once you copy them.
Key Challenges Addressed
Two things make UI animation hard in practice: getting the physics right, and knowing which pattern to reach for.
Timing that feels wrong: Most engineers default to CSS ease-in-out or a fixed-duration Framer Motion fade. This works for simple opacity changes, but for anything involving position, scale, or layout, it reads as stiff. Physical objects don't decelerate uniformly — they overshoot, they respond to velocity. Skills targets this with spring-physics configurations tuned for common UI scenarios.
Interaction patterns with no reference implementation: Swipe-to-dismiss, drag reorder, shared element transitions — these are well-documented in native iOS/Android SDKs but poorly documented for the web. Skills covers full implementations, including the failure modes you'll hit after the happy path.
Layout animation coordination: Animating elements as they enter, leave, or reorder in a list is hard to do without visual glitches. Framer Motion's AnimatePresence and layout animation features handle this well, but using them correctly requires specific composition patterns that aren't obvious from the documentation alone.
Getting Started
Clone the repo and run it locally to see all patterns in context:
git clone https://github.com/emilkowalski/skills.git
cd skills
npm install
npm run dev
Sharp edges you'll hit early:
- Framer Motion version pinning: Copying a pattern into a project on a different major Framer Motion version silently breaks animations —
AnimatePresenceexit callbacks anduseAnimatesignatures changed between v10 and v11. Runnpm list framer-motionin both codebases before copying any pattern. - Node version: Node version mismatches surface as cryptic CSS/JS optimization errors in Next.js builds, not obvious version complaints. Match the
.nvmrcversion or theengines.nodefield inpackage.jsonexactly.
Once it's running, each skill renders in isolation so you can inspect its behavior without surrounding page noise. That's intentional — treat it like a sandboxed component demo.
Features and Use Cases
Spring-physics animations: Pre-tuned configurations for common UI movements — panel slide-in, modal entrance, card hover lift. These use actual spring physics so the motion feels responsive to simulated inertia rather than just decelerated timing curves.
Gesture-driven interactions: Patterns for drag, press, and swipe that respond correctly to input velocity, not just position. If you've built a swipe gesture that works on desktop but feels wrong on mobile because it ignores gesture speed, these implementations address that gap directly.
Layout animations: Techniques for animating list reordering, content expansion, and collapsing panels without frame drops. The patterns clarify when to use the layout prop versus layoutId for shared-element transitions — a distinction the Framer Motion docs explain but don't illustrate well in realistic component compositions.
Micro-interactions: Smaller patterns — focus ring animations, button press feedback, checkbox state transitions — that layer onto existing components without conflicting with their structure.
Realistic use cases: a SaaS dashboard where the command palette needs to feel snappy and precise; a mobile web app where card interactions need to match native gesture expectations; an existing design system you want to upgrade from flat CSS transitions to physics-based motion.
Ecosystem and Dependencies
Skills is built on a stack common to contemporary design-engineering work:
- Framer Motion: The animation engine for everything in the repo. You'll get the most value here if you're already committed to this library. The Framer Motion documentation covers the full API and pairs well with React's component model.
- Next.js: The demo app runs on Next.js. The component patterns themselves are framework-agnostic React and port cleanly to Vite or any other React environment without changes.
- Tailwind CSS: Class-based styling throughout, which keeps visual logic cleanly separated from animation logic when adapting patterns.
- TypeScript: Types are included in the implementations. You won't need to reconstruct them when copying into a typed codebase.
The Framer Motion + Tailwind + Next.js combination is the current consensus stack for this kind of work. If you're already on it, copying from Skills into your project is nearly frictionless.
Architectural Overview
The repo is structured as a Next.js app where each skill or technique gets its own page or route. The component architecture is deliberately flat: each pattern lives in a single file (or a small cluster of co-located files) with animation logic inline rather than abstracted into hooks or utility layers.
This is the right call for a reference project. Abstraction hides intent. When you're reading these patterns to learn or adapt them, seeing the Framer Motion props directly on the motion component — rather than buried in a custom hook — tells you exactly what each parameter does and why.
There's no state management library, no API integration, no routing complexity beyond Next.js defaults. Every bit of complexity in the codebase is there because the pattern itself requires it. You won't mistake scaffolding complexity for pattern complexity.
Pros and Cons
Pros
- Patterns reflect decisions made when shipping interfaces where visual quality is a product requirement — not toy examples.
- Short, readable code with no framework abstractions to unwrap before you reach the interesting part.
- You own the code completely once you copy it — no upstream dependency surprises, no API deprecations that silently break your UI.
- Curated, not exhaustive. High signal-to-noise ratio compared to broader UI component repos.
Cons
- No installable package. You copy and own the code, which means you're responsible for keeping patterns current as Framer Motion evolves.
- Deep Framer Motion coupling. Adapting any pattern to GSAP, CSS keyframes, or another animation engine is a rewrite, not a port.
- Not a component library. No form controls, data tables, layout primitives, or navigation components. Skills is specifically about animation and interaction mechanics.
- Sparse explanation. The patterns are clean but lightly commented. If you're learning Framer Motion simultaneously, you'll need the library docs open alongside to understand why each parameter value was chosen.
Comparison and Alternatives
GitHub stars
Weekly star gains
Full metrics details
GitHub GraphQL current stargazer cohort, reconstructed from starredAt events and totalCount retrieved in one stable pagination walk. GitHub does not expose historical unstar events, so this is not an exact ledger of past star counts.
Skills
GitHub GraphQL current stargazer cohort, reconstructed from starredAt events and totalCount retrieved in one stable pagination walk. GitHub does not expose historical unstar events, so this is not an exact ledger of past star counts.
127 observed daily rows. Missing days are not fabricated.
| Date | Value |
|---|
shadcn/ui
GitHub GraphQL current stargazer cohort, reconstructed from starredAt events and totalCount retrieved in one stable pagination walk. GitHub does not expose historical unstar events, so this is not an exact ledger of past star counts.
180 observed daily rows. Missing days are not fabricated.
| Date | Value |
|---|
Magic UI
GitHub GraphQL current stargazer cohort, reconstructed from starredAt events and totalCount retrieved in one stable pagination walk. GitHub does not expose historical unstar events, so this is not an exact ledger of past star counts.
43 observed daily rows. Missing days are not fabricated.
| Date | Value |
|---|
Issues opened
Weekly total
Full metrics details
GitHub API observation. Historical values render only when the source provides a real observation for that day.
Skills
GitHub API observation. Historical values render only when the source provides a real observation for that day.
126 observed daily rows. Missing days are not fabricated.
| Date | Value |
|---|
shadcn/ui
GitHub API observation. Historical values render only when the source provides a real observation for that day.
177 observed daily rows. Missing days are not fabricated.
| Date | Value |
|---|
Issues closed
Weekly total
Full metrics details
GitHub Search issue totals queried for exact UTC days; rolling values are sums of proven daily counts.
Skills
GitHub Search issue totals queried for exact UTC days; rolling values are sums of proven daily counts.
126 observed daily rows. Missing days are not fabricated.
| Date | Value |
|---|
shadcn/ui
GitHub Search issue totals queried for exact UTC days; rolling values are sums of proven daily counts.
177 observed daily rows. Missing days are not fabricated.
| Date | Value |
|---|
Pull requests opened
Weekly total
Full metrics details
GitHub API observation. Historical values render only when the source provides a real observation for that day.
Skills
GitHub API observation. Historical values render only when the source provides a real observation for that day.
126 observed daily rows. Missing days are not fabricated.
| Date | Value |
|---|
shadcn/ui
GitHub API observation. Historical values render only when the source provides a real observation for that day.
177 observed daily rows. Missing days are not fabricated.
| Date | Value |
|---|
Pull requests closed
Weekly total
Full metrics details
GitHub Search pull-request totals queried for exact UTC days; rolling values are sums of proven daily counts.
Skills
GitHub Search pull-request totals queried for exact UTC days; rolling values are sums of proven daily counts.
126 observed daily rows. Missing days are not fabricated.
| Date | Value |
|---|
shadcn/ui
GitHub Search pull-request totals queried for exact UTC days; rolling values are sums of proven daily counts.
177 observed daily rows. Missing days are not fabricated.
| Date | Value |
|---|
Pull requests merged
Weekly total
Full metrics details
GitHub API observation. Historical values render only when the source provides a real observation for that day.
Skills
GitHub API observation. Historical values render only when the source provides a real observation for that day.
126 observed daily rows. Missing days are not fabricated.
| Date | Value |
|---|
shadcn/ui
GitHub API observation. Historical values render only when the source provides a real observation for that day.
177 observed daily rows. Missing days are not fabricated.
| Date | Value |
|---|
Open/closed pull request ratio
Weekly average
Full metrics details
GitHub Search pull-request totals queried for exact UTC days; rolling values are sums of proven daily counts.
Skills
GitHub Search pull-request totals queried for exact UTC days; rolling values are sums of proven daily counts.
127 observed daily rows. Missing days are not fabricated.
| Date | Value |
|---|
shadcn/ui
GitHub Search pull-request totals queried for exact UTC days; rolling values are sums of proven daily counts.
180 observed daily rows. Missing days are not fabricated.
| Date | Value |
|---|
Magic UI
GitHub Search pull-request totals queried for exact UTC days; rolling values are sums of proven daily counts.
43 observed daily rows. Missing days are not fabricated.
| Date | Value |
|---|
Open/closed issues ratio
Weekly average
Full metrics details
GitHub Search issue totals queried for exact UTC days; rolling values are sums of proven daily counts.
Skills
GitHub Search issue totals queried for exact UTC days; rolling values are sums of proven daily counts.
127 observed daily rows. Missing days are not fabricated.
| Date | Value |
|---|
shadcn/ui
GitHub Search issue totals queried for exact UTC days; rolling values are sums of proven daily counts.
180 observed daily rows. Missing days are not fabricated.
| Date | Value |
|---|
Magic UI
GitHub Search issue totals queried for exact UTC days; rolling values are sums of proven daily counts.
43 observed daily rows. Missing days are not fabricated.
| Date | Value |
|---|
Releases
Weekly total
Full metrics details
GitHub release published_at events bucketed by UTC day; drafts are excluded.
Skills
GitHub release published_at events bucketed by UTC day; drafts are excluded.
127 observed daily rows. Missing days are not fabricated.
| Date | Value |
|---|
shadcn/ui
GitHub release published_at events bucketed by UTC day; drafts are excluded.
180 observed daily rows. Missing days are not fabricated.
| Date | Value |
|---|
Magic UI
GitHub release published_at events bucketed by UTC day; drafts are excluded.
43 observed daily rows. Missing days are not fabricated.
| Date | Value |
|---|
Hotness score
Weekly average
Full metrics details
Derived only from GitHub GraphQL starredAt events after daily star totals are reconciled.
Skills
Derived only from GitHub GraphQL starredAt events after daily star totals are reconciled.
127 observed daily rows. Missing days are not fabricated.
Per-day formula: 0.40 × Hot today + 0.40 × Hot week + 0.20 × Breakout.
- Same-day multiplier = stars 1d ÷ max(1, stars 7d ÷ 7)
- Weekly multiplier = stars 7d ÷ max(1, stars 30d ÷ 30 × 7)
- Fortnight multiplier = stars 14d ÷ max(1, stars 90d ÷ 90 × 14)
- Hot today = clamp(70 × log-scale(stars 1d, 1000) + 30 × breakout-scale(same-day, 4))
- Hot week = clamp(75 × log-scale(stars 7d, 5000) + 25 × breakout-scale(weekly, 3))
- Breakout = clamp(35 × breakout-scale(same-day, 4) + 40 × breakout-scale(weekly, 3) + 25 × breakout-scale(fortnight, 2.5))
| Date | Stars | Stars 1d | Stars 7d | Stars 14d | Stars 30d | Stars 90d | Same-day multiplier | Weekly multiplier | Fortnight multiplier | Hot today | Hot week | Breakout | Stored score |
|---|
shadcn/ui
Derived only from GitHub GraphQL starredAt events after daily star totals are reconciled.
180 observed daily rows. Missing days are not fabricated.
Per-day formula: 0.40 × Hot today + 0.40 × Hot week + 0.20 × Breakout.
- Same-day multiplier = stars 1d ÷ max(1, stars 7d ÷ 7)
- Weekly multiplier = stars 7d ÷ max(1, stars 30d ÷ 30 × 7)
- Fortnight multiplier = stars 14d ÷ max(1, stars 90d ÷ 90 × 14)
- Hot today = clamp(70 × log-scale(stars 1d, 1000) + 30 × breakout-scale(same-day, 4))
- Hot week = clamp(75 × log-scale(stars 7d, 5000) + 25 × breakout-scale(weekly, 3))
- Breakout = clamp(35 × breakout-scale(same-day, 4) + 40 × breakout-scale(weekly, 3) + 25 × breakout-scale(fortnight, 2.5))
| Date | Stars | Stars 1d | Stars 7d | Stars 14d | Stars 30d | Stars 90d | Same-day multiplier | Weekly multiplier | Fortnight multiplier | Hot today | Hot week | Breakout | Stored score |
|---|
Magic UI
Derived only from GitHub GraphQL starredAt events after daily star totals are reconciled.
43 observed daily rows. Missing days are not fabricated.
Per-day formula: 0.40 × Hot today + 0.40 × Hot week + 0.20 × Breakout.
- Same-day multiplier = stars 1d ÷ max(1, stars 7d ÷ 7)
- Weekly multiplier = stars 7d ÷ max(1, stars 30d ÷ 30 × 7)
- Fortnight multiplier = stars 14d ÷ max(1, stars 90d ÷ 90 × 14)
- Hot today = clamp(70 × log-scale(stars 1d, 1000) + 30 × breakout-scale(same-day, 4))
- Hot week = clamp(75 × log-scale(stars 7d, 5000) + 25 × breakout-scale(weekly, 3))
- Breakout = clamp(35 × breakout-scale(same-day, 4) + 40 × breakout-scale(weekly, 3) + 25 × breakout-scale(fortnight, 2.5))
| Date | Stars | Stars 1d | Stars 7d | Stars 14d | Stars 30d | Stars 90d | Same-day multiplier | Weekly multiplier | Fortnight multiplier | Hot today | Hot week | Breakout | Stored score |
|---|
Reliability score
Weekly average
Full metrics details
Derived from the displayed continuity, closure, shipping, liveness, and support-burden components.
Skills
Derived from the displayed continuity, closure, shipping, liveness, and support-burden components.
127 observed daily rows. Missing days are not fabricated.
Per-day formula: 0.30 × Continuity + 0.30 × Closure + 0.20 × Shipping + 0.10 × Liveness + 0.10 × Support burden.
- Continuity = 35% active ratio 90d + 20% active ratio 30d + 15% PR efficiency 90d + 10% PR efficiency 30d + 10% liveness + 10% observed-history coverage
- Closure = 55% PR merge efficiency 90d + 45% issue close efficiency 90d
- Shipping = 100 × (20% × release ratio 30d + 30% × release ratio 90d + 50% × release ratio 180d); ratios are releases ÷ 2, 6, and 12, capped at 1
- Support burden = 100 − 2 × open issues per 1,000 stars
- Liveness = 100 × exp(−pushed days ago ÷ 120)
Stars, issues, pull requests, and releases are daily observations. Pushed-days and license are repository snapshot-derived inputs and are not independent historical GitHub events.
| Date | Issues opened | Issues closed | PRs opened | PRs merged | Releases | Stars | Open issues | Pushed days ago | Continuity | Closure | Shipping | Liveness | Support burden | License | Observed days | Missing days | Needs healing | Stored score |
|---|
shadcn/ui
Derived from the displayed continuity, closure, shipping, liveness, and support-burden components.
180 observed daily rows. Missing days are not fabricated.
Per-day formula: 0.30 × Continuity + 0.30 × Closure + 0.20 × Shipping + 0.10 × Liveness + 0.10 × Support burden.
- Continuity = 35% active ratio 90d + 20% active ratio 30d + 15% PR efficiency 90d + 10% PR efficiency 30d + 10% liveness + 10% observed-history coverage
- Closure = 55% PR merge efficiency 90d + 45% issue close efficiency 90d
- Shipping = 100 × (20% × release ratio 30d + 30% × release ratio 90d + 50% × release ratio 180d); ratios are releases ÷ 2, 6, and 12, capped at 1
- Support burden = 100 − 2 × open issues per 1,000 stars
- Liveness = 100 × exp(−pushed days ago ÷ 120)
Stars, issues, pull requests, and releases are daily observations. Pushed-days and license are repository snapshot-derived inputs and are not independent historical GitHub events.
| Date | Issues opened | Issues closed | PRs opened | PRs merged | Releases | Stars | Open issues | Pushed days ago | Continuity | Closure | Shipping | Liveness | Support burden | License | Observed days | Missing days | Needs healing | Stored score |
|---|
Magic UI
Derived from the displayed continuity, closure, shipping, liveness, and support-burden components.
43 observed daily rows. Missing days are not fabricated.
Per-day formula: 0.30 × Continuity + 0.30 × Closure + 0.20 × Shipping + 0.10 × Liveness + 0.10 × Support burden.
- Continuity = 35% active ratio 90d + 20% active ratio 30d + 15% PR efficiency 90d + 10% PR efficiency 30d + 10% liveness + 10% observed-history coverage
- Closure = 55% PR merge efficiency 90d + 45% issue close efficiency 90d
- Shipping = 100 × (20% × release ratio 30d + 30% × release ratio 90d + 50% × release ratio 180d); ratios are releases ÷ 2, 6, and 12, capped at 1
- Support burden = 100 − 2 × open issues per 1,000 stars
- Liveness = 100 × exp(−pushed days ago ÷ 120)
Stars, issues, pull requests, and releases are daily observations. Pushed-days and license are repository snapshot-derived inputs and are not independent historical GitHub events.
| Date | Issues opened | Issues closed | PRs opened | PRs merged | Releases | Stars | Open issues | Pushed days ago | Continuity | Closure | Shipping | Liveness | Support burden | License | Observed days | Missing days | Needs healing | Stored score |
|---|
For teams that want something more installable or with broader scope, several strong alternatives exist.
shadcn/ui takes a similar copy-paste philosophy but focuses on accessibility-first component primitives using Radix UI under the hood. It's broader in scope — full form controls, navigation, dialogs — and has extensive documentation. If you need a full component system rather than an animation reference, start here.
Framer Motion itself is the underlying animation library Skills uses. If you want to build patterns from scratch rather than adapt existing ones, the Framer Motion documentation and examples cover the full API surface. Skills is a curated cookbook built on top of it.
Magic UI offers animated components as installable packages, also built on Framer Motion and Tailwind. If you want animated components you can add via a CLI command rather than copying implementations by hand, Magic UI fits that workflow.
animate.css is a CSS-only animation library requiring no JavaScript runtime. It's useful for simple entrance/exit animations in projects without a React dependency, but it doesn't address spring physics, gesture-driven interactions, or layout animations.
| Project | Repo | Hotness (~6mo) | PRs merged (~6mo) | Star growth (~6mo) |
|---|---|---|---|---|
| Skills | https://github.com/emilkowalski/skills | 39.39 | 3 | 16803 |
| shadcn/ui | https://github.com/shadcn-ui/ui | 99.60 | 436 | 5851 |
| Framer Motion | https://github.com/framer/motion | metrics pending | 0 | metrics pending |
| Magic UI | https://github.com/magicuidesign/magicui | metrics pending | 0 | 553 |
| animate.css | https://github.com/animate-css/animate.css | metrics pending | 0 | 22 |
The best overall alternative is shadcn/ui. It has the most complete documentation, the largest contributor community, and a similarly opinionated copy-paste delivery model. It won't match Skills' depth on animation mechanics, but as a foundation for a full design system it's far more complete.
Conclusion
If you build React interfaces where interaction quality matters and you're already using Framer Motion, Skills is the most direct reference available for production-grade animation patterns. Read the project on GitHub, find a pattern that matches your use case, and adapt it directly — you'll skip weeks of trial-and-error on spring tuning.
Do not copy patterns wholesale across major Framer Motion version boundaries. If your project runs Framer Motion v10 and the Skills repo has moved to v11, AnimatePresence exit behavior and layout animation sizing will diverge silently — elements snap to wrong positions or animations skip entirely. Before copying any pattern, run npm list framer-motion in both your project and the Skills repo and match the major version.
