en un clic
animations
Create gorgeous micro-interactions and animations using CSS and Svelte transitions. Use this skill when adding motion to components, page transitions, scroll animations, or any interactive elements that need polish.
Create gorgeous micro-interactions and animations using CSS and Svelte transitions. Use this skill when adding motion to components, page transitions, scroll animations, or any interactive elements that need polish.
Guides distinctive brand colors, typography, and visual choices. Contains human-first design principles to ensure sites look intentional and memorable.
Write human-sounding marketing copy that converts. Contains overused word alternatives, headline formulas, CTA patterns, and tone guidelines for specific, authentic text.
Maintain clear, non-technical documentation for website projects. Use this skill after ANY code change to update SITE.md with what changed. Triggers on file edits, new pages, component changes, or when the user asks about their site. Essential for keeping non-developers informed about their project.
Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that feels intentional and memorable.
Section architecture, hero patterns, layout principles, and conversion optimization for marketing landing pages. Ensures strategic, high-converting page structure.
Guided discovery flow for new Ship Studio projects. Asks about business, audience, brand personality, and goals to create a personalized SITE.md and build plan.
| name | animations |
| description | Create gorgeous micro-interactions and animations using CSS and Svelte transitions. Use this skill when adding motion to components, page transitions, scroll animations, or any interactive elements that need polish. |
| user_invocable | true |
| invocation | /animate |
This skill guides the creation of beautiful, purposeful animations using CSS and Svelte's built-in transition system. Great animations feel natural, add delight, and improve user experience.
Animations should be:
Avoid:
| Type | Duration | Use Case |
|---|---|---|
| Micro | 100-150ms | Button hovers, icon changes |
| Fast | 200-300ms | Fade ins, small movements |
| Medium | 300-500ms | Page elements, cards |
| Slow | 500-800ms | Page transitions, large elements |
/* Smooth and natural */
--ease-out: cubic-bezier(0.33, 1, 0.68, 1);
/* Snappy entrance */
--ease-out-back: cubic-bezier(0.34, 1.56, 0.64, 1);
/* Elegant deceleration */
--ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
<script lang="ts">
import { fade, fly, slide, scale, blur } from 'svelte/transition';
let visible = $state(true);
</script>
{#if visible}
<div transition:fade={{ duration: 300 }}>Fades in and out</div>
{/if}
{#if visible}
<div in:fade={{ duration: 300, delay: 0 }}>Content</div>
{/if}
{#if visible}
<div in:fly={{ y: 20, duration: 400 }}>Content slides up</div>
{/if}
{#if visible}
<div in:scale={{ start: 0.95, duration: 300 }}>Content scales in</div>
{/if}
<script lang="ts">
import { cubicOut } from 'svelte/easing';
function customFly(node: HTMLElement, { delay = 0, duration = 400 }) {
return {
delay,
duration,
easing: cubicOut,
css: (t: number) => `
transform: translateY(${(1 - t) * 20}px);
opacity: ${t};
`
};
}
</script>
{#if visible}
<div in:customFly={{ duration: 400 }}>Custom animation</div>
{/if}
<button class="hover:scale-[1.02] active:scale-[0.98] transition-transform duration-150">
Click me
</button>
<div class="fade-up">Content</div>
<style>
.fade-up {
animation: fadeUp 0.5s cubic-bezier(0.33, 1, 0.68, 1) forwards;
}
@keyframes fadeUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>
<ul>
{#each items as item, i}
<li class="stagger-item" style="animation-delay: {i * 100}ms">
{item}
</li>
{/each}
</ul>
<style>
.stagger-item {
opacity: 0;
animation: fadeUp 0.4s cubic-bezier(0.33, 1, 0.68, 1) forwards;
}
.stagger-item:nth-child(1) {
animation-delay: 0ms;
}
.stagger-item:nth-child(2) {
animation-delay: 100ms;
}
.stagger-item:nth-child(3) {
animation-delay: 200ms;
}
.stagger-item:nth-child(4) {
animation-delay: 300ms;
}
@keyframes fadeUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>
<div class="h-4 bg-gray-200 rounded skeleton"></div>
<style>
.skeleton {
animation: pulse 1.5s ease-in-out infinite;
}
@keyframes pulse {
0%,
100% {
opacity: 0.5;
}
50% {
opacity: 1;
}
}
</style>
<script lang="ts">
import { onMount } from 'svelte';
let element: HTMLElement;
let isVisible = $state(false);
onMount(() => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
isVisible = true;
observer.unobserve(entry.target);
}
});
},
{ threshold: 0.1, rootMargin: '-50px' }
);
observer.observe(element);
return () => observer.disconnect();
});
</script>
<section
bind:this={element}
class="transition-all duration-500 ease-out {isVisible
? 'opacity-100 translate-y-0'
: 'opacity-0 translate-y-8'}"
>
Content appears on scroll
</section>
<!-- src/lib/components/InView.svelte -->
<script lang="ts">
import { onMount } from 'svelte';
import { browser } from '$app/environment';
interface Props {
children: import('svelte').Snippet;
threshold?: number;
rootMargin?: string;
once?: boolean;
}
let { children, threshold = 0.1, rootMargin = '-50px', once = true }: Props = $props();
let element: HTMLElement;
let isVisible = $state(false);
let prefersReducedMotion = $state(false);
onMount(() => {
// Respect user's motion preferences
prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (prefersReducedMotion) {
isVisible = true;
return;
}
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
isVisible = true;
if (once) observer.unobserve(entry.target);
} else if (!once) {
isVisible = false;
}
});
},
{ threshold, rootMargin }
);
observer.observe(element);
return () => observer.disconnect();
});
</script>
<div
bind:this={element}
class="transition-all duration-500 ease-out {isVisible
? 'opacity-100 translate-y-0'
: 'opacity-0 translate-y-6'}"
>
{@render children()}
</div>
Version Requirement: The
$app/statemodule requires SvelteKit 2.12+. For earlier versions, use$app/storeswith the$pagestore instead.
<!-- src/routes/+layout.svelte -->
<script lang="ts">
import { page } from '$app/state';
import { fade } from 'svelte/transition';
let { children } = $props();
</script>
{#key page.url.pathname}
<div in:fade={{ duration: 200, delay: 200 }} out:fade={{ duration: 200 }}>
{@render children()}
</div>
{/key}
Note: In SvelteKit 2.12+, use
$app/stateinstead of$app/stores. Thepageobject is reactive without needing the$pagestore prefix.
Prefer transform and opacity - These are GPU-accelerated:
/* Good */
transform: translateX(10px);
opacity: 0.5;
/* Avoid */
left: 10px;
width: 100px;
Use will-change sparingly:
.animated-element {
will-change: transform;
}
Respect reduced motion:
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
Avoid animating on scroll without throttling - Use Intersection Observer with once: true.
prefers-reduced-motion