[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6D21X39JPEYP74KDAXVG82Y":3,"skill-same:01M6D21X39JPEYP74KDAXVG82Y":77},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":28,"publishedAt":49,"linkOnly":50,"markdownContent":51,"markdownBody":52,"frontMatter":53,"refName":54,"commitSha":55,"skillDirectory":56,"forksCount":57,"repositoryHtmlUrl":58,"repositoryHomepageUrl":59,"ownerHtmlUrl":60,"ownerBlogUrl":59,"ownerDescription":61,"updatedAt":62,"install":63,"resources":76},"01M6D21X39JPEYP74KDAXVG82Y","motion-stacks",null,"Writing motion in each stack: plain CSS and JavaScript, Tailwind, React and the Motion library, Next.js, Vue, Svelte, and the native mobile toolkits, each with reduced motion. Read before writing animation code in the project's stack.","enowdev\u002Fenowxcli","enowdev","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F179420349?v=4","01M6D1MVX2KC0X3Z9NESE7P22K","Enow Dev.","01M6D1MW1FMZGBDKWAJY4GQX81",67,"Apache-2.0","https:\u002F\u002Fgithub.com\u002Fenowdev\u002Fenowxcli\u002Fblob\u002Fmain\u002Fcrates\u002Fenowx-core\u002Fskills\u002Fmotion-stacks\u002FSKILL.md",[18,23],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000009","前端开发","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":26,"taxonomyTypeDesc":27},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48],"motion stacks","motion","js","svelte","project","next","mobile","read","toolkits","stack","writing","Next.js","Tailwind","Animation","vue","library","code","react","javascript","css","2026-11-10 10:12:30",false,"---\nname: motion-stacks\ndescription: \"Writing motion in each stack: plain CSS and JavaScript, Tailwind, React and the Motion library, Next.js, Vue, Svelte, and the native mobile toolkits, each with reduced motion. Read before writing animation code in the project's stack.\"\n---\n\n# Motion in each stack\n\nThe rules are the same in every stack (`motion`); this is how each one\nwrites them. Whatever the stack: the tokens are defined once, reduced\nmotion is handled once at the root rather than in every component, only\nopacity and transform move, and no library is added for what CSS already\ndoes (`motion-performance`).\n\n## 1. Plain CSS and JavaScript\n\n- The tokens, in one place:\n\n```css\n:root {\n  --m-ease-out: cubic-bezier(0.22, 0.61, 0.36, 1);\n  --m-ease-in: cubic-bezier(0.55, 0.055, 0.675, 0.19);\n  --m-ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);\n  --m-instant: 100ms;\n  --m-fast: 160ms;\n  --m-base: 240ms;\n  --m-panel: 320ms;\n  --m-enter: 700ms;\n  --m-draw: 1000ms;\n  --m-shift-sm: 6px;\n  --m-shift: 12px;\n  --m-shift-lg: 18px;\n}\n```\n\n- Keyframes named for what they do (`rise`, `fade`, `draw`, `count`), with\n  only a `from` when they end at the element's own style.\n- Movement written inside `@media (prefers-reduced-motion: no-preference)`\n  rather than undone inside `reduce`: a rule added later is then safe by\n  default.\n- The reveal module from `motion-reveal`, started once:\n  `startReveal(document.documentElement)` at the end of `\u003Cbody>` or on\n  `DOMContentLoaded`.\n- Elements shown and hidden with `display` or `[open]` (dialogs, popovers)\n  animate in and out with `@starting-style` and `allow-discrete`\n  transitions (`motion-interface`).\n- One-off motion from script uses the Web Animations API, not a\n  `requestAnimationFrame` loop writing styles:\n  `el.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 240, easing:\n  \"cubic-bezier(0.22, 0.61, 0.36, 1)\" })`. It runs like CSS and returns an\n  animation you can await (`.finished`), `cancel()` or `reverse()`.\n- Replaying CSS animations (a drawing, on hover): give the parts\n  `animation-fill-mode: both`, then for each of\n  `el.getAnimations({ subtree: true })` set `currentTime = 0` and call\n  `play()`. A finished animation with `backwards` fill is no longer in\n  effect, so `getAnimations()` does not return it: replayable parts need\n  `both`.\n- Reduced motion in script: `matchMedia(\"(prefers-reduced-motion:\n  reduce)\")`, and its `change` event for anything that keeps running.\n\n## 2. Tailwind CSS\n\nVersion 4 keeps the tokens in CSS:\n\n```css\n@theme {\n  --ease-soft-out: cubic-bezier(0.22, 0.61, 0.36, 1);\n  --ease-soft-in-out: cubic-bezier(0.65, 0, 0.35, 1);\n  --animate-rise: rise 700ms var(--ease-soft-out) backwards;\n  --animate-fade: fade 600ms var(--ease-soft-out) backwards;\n  @keyframes rise {\n    from { opacity: 0; transform: translateY(12px); }\n  }\n  @keyframes fade {\n    from { opacity: 0; }\n  }\n}\n```\n\n- That gives `ease-soft-out`, `animate-rise` and `animate-fade` as\n  utilities: `transition-colors duration-150 ease-soft-out`,\n  `motion-safe:animate-rise`.\n- `motion-safe:` on every animation and every transform transition;\n  `motion-reduce:transition-none` or `motion-reduce:animate-none` where a\n  component library adds its own.\n- A revealed block's children: `group` on the block, then\n  `motion-safe:group-data-[shown]:animate-rise` on the child. The hidden\n  pre-state and the arrival delay are clearer as the dozen lines of CSS in\n  `motion-reveal` in the global stylesheet than as long utility strings on\n  every element.\n- Version 3: the same tokens in `theme.extend` (`transitionTimingFunction`,\n  `keyframes`, `animation`) in `tailwind.config`; `motion-safe:` and\n  `motion-reduce:` are the same.\n- shadcn\u002Fui's overlays use `tw-animate-css` (formerly\n  `tailwindcss-animate`): `data-[state=open]:animate-in fade-in-0\n  zoom-in-95`, `data-[state=closed]:animate-out fade-out-0 zoom-out-95`,\n  with `duration-200`. Keep the distances small (`slide-in-from-top-2`) and\n  leave its defaults when they already match `motion-interface`.\n\n## 3. React\n\n- The reveal hook sets the flag before paint and starts the one observer:\n\n```tsx\nexport function useReveal(page: RefObject\u003CHTMLElement | null>) {\n  useLayoutEffect(() => {\n    const root = page.current;\n    return root ? startReveal(root) : undefined; \u002F\u002F motion-reveal, section 2\n  }, [page]);\n}\n```\n\n  `useLayoutEffect`, not `useEffect`, for anything that sets a hidden\n  pre-state: it runs before the browser paints.\n- Per-frame values never pass through the state of a large tree. A clock\n  that ticks ten times a second (a scripted demo, `motion-demo`) lives in\n  the small component that shows it; a parent that needs to follow along\n  changes state only when something it shows changes (a phase, a few times\n  per loop), through a callback in an effect.\n- Continuous values (the pointer, a drag, a progress) go into refs or CSS\n  variables on an element (`el.style.setProperty(\"--x\", ...)`), never into\n  `setState` sixty times a second.\n- Replay from an event handler with `getAnimations()` on a ref.\n- Changing a `key` remounts and replays mount animations: use it on\n  purpose, never by accident in a list.\n- Strict mode runs effects twice in development: every observer\n  disconnects in its cleanup.\n\n### The Motion library (`motion`, formerly Framer Motion)\n\n- It earns its weight for: exits of components React removes\n  (`AnimatePresence`), layout animation (an element changing size or place\n  smoothly, `layout`), shared elements between views (`layoutId`), and\n  gestures with springs (drag with momentum). Not for fades and reveals\n  that CSS does.\n- Size: import from `motion\u002Freact`, wrap the app in `LazyMotion` with\n  `domAnimation` (`domMax` only for layout and drag) and use `m.div`, not\n  `motion.div`. That keeps it to a fraction of the full component's size.\n- Reduced motion once, at the root: `\u003CMotionConfig reducedMotion=\"user\">`\n  turns off transform and layout animation and keeps opacity;\n  `useReducedMotion()` for choices in code.\n- Reveals: `whileInView` with `viewport={{ once: true, margin: \"0px 0px\n  -10% 0px\" }}` and `initial={{ opacity: 0, y: 12 }}`. The `initial` state\n  is rendered on the server, so that content is hidden until the page\n  hydrates: give content above the fold `initial={false}`, and accept that\n  hidden text below it is invisible without JavaScript, or use the CSS\n  reveal instead.\n- Exits: `\u003CAnimatePresence>` around conditionally rendered children, with\n  `exit={{ opacity: 0 }}`; `mode=\"wait\"` when one view replaces another.\n- Timing from the tokens, kept in one object that mirrors the CSS:\n  `transition={{ duration: 0.24, ease: [0.22, 0.61, 0.36, 1] }}`. Springs\n  for drags and layout, critically damped so nothing overshoots:\n  `{ type: \"spring\", stiffness: 400, damping: 40 }`.\n\n### Other libraries\n\n- GSAP for a long scripted timeline with many parts, with\n  `gsap.matchMedia()` for reduced motion; ScrollTrigger only when CSS\n  scroll-driven animations cannot do the job.\n- Lottie for a designer-made animation, with the lighter dotLottie player\n  rather than the full `lottie-web`, and the first or last frame shown\n  under reduced motion.\n- Each is a cost you state in the report (`motion-performance`).\n\n## 4. Next.js\n\n- Motion lives in client components; keep the `\"use client\"` boundary to\n  the animated part (a `Reveal` wrapper, the demo), not the whole page.\n- Server-rendered markup paints before hydration. A hidden pre-state needs\n  the inline head script with its safety timeout (`motion-reveal`,\n  section 3), written into `\u003Chead>` in `app\u002Flayout.tsx` with\n  `dangerouslySetInnerHTML`, and `suppressHydrationWarning` on `\u003Chtml>`,\n  since the attribute differs from the server's.\n- Route transitions: the View Transitions API where the version supports\n  it (recent versions have an experimental view transitions flag used with\n  React's `ViewTransition` component), or `document.startViewTransition`\n  around the navigation. No fade on every route.\n- `next\u002Ffont` so a font swap does not move text in the middle of an\n  entrance; `next\u002Fimage` with sizes so an image does not shift a block\n  while it reveals.\n- `loading.tsx` shows only when the wait is worth showing\n  (`motion-interface`, loading).\n\n## 5. Vue\n\n- `\u003CTransition name=\"fade\">` with:\n\n```css\n.fade-enter-active,\n.fade-leave-active { transition: opacity var(--m-base) var(--m-ease-out); }\n.fade-enter-from,\n.fade-leave-to { opacity: 0; }\n```\n\n  and `appear` to play on the first render.\n- `\u003CTransitionGroup tag=\"ul\" name=\"list\">` for lists, with `.list-move {\n  transition: transform var(--m-base) var(--m-ease-out); }` for reordering\n  and `.list-leave-active { position: absolute; }` so the others close the\n  gap.\n- Reduced motion: `usePreferredReducedMotion()` from VueUse, with a `none`\n  transition name when it is `reduce`, or the transition classes inside the\n  no-preference media query.\n- A `v-reveal` directive that adds its element to the page's one observer.\n- Nuxt: short `pageTransition` and `layoutTransition`; the reveal flag from\n  an inline head script through `useHead`.\n\n## 6. Svelte (5)\n\n- `transition:fade={{ duration: 240 }}`, `in:fly={{ y: 12, duration: 240,\n  easing: cubicOut }}`, `out:fade={{ duration: 160 }}`, and\n  `animate:flip={{ duration: 240 }}` inside a keyed `{#each}` for\n  reordering.\n- Custom transitions return `css: (t) => ...`, so they run as CSS rather\n  than per-frame JavaScript.\n- Reduced motion: `import { prefersReducedMotion } from \"svelte\u002Fmotion\"`,\n  then `duration: prefersReducedMotion.current ? 0 : 240`.\n- `Tween` and `Spring` from `svelte\u002Fmotion` for values that animate (a\n  number, a position): `new Tween(0, { duration: 400, easing: cubicOut })`.\n- `{#key value}` replays intro transitions when the value changes: only on\n  purpose.\n- SvelteKit: `onNavigate` with `document.startViewTransition` for route\n  transitions.\n\n## 7. Native mobile, briefly\n\n- The platform's own navigation and sheet transitions win over web habits.\n- React Native: Reanimated, which runs on the UI thread. `withTiming(value,\n  { duration: 240, easing: Easing.bezier(0.22, 0.61, 0.36, 1),\n  reduceMotion: ReduceMotion.System })`; `useReducedMotion()` for choices;\n  layout animations such as\n  `entering={FadeIn.duration(240).reduceMotion(ReduceMotion.System)}`.\n- Flutter: implicit animations (`AnimatedOpacity`, `AnimatedContainer`,\n  `AnimatedSwitcher`) for changes of state, `Curves.easeOutCubic` close to\n  the tokens, and `MediaQuery.disableAnimationsOf(context)` to set\n  durations to zero.\n- SwiftUI: `withAnimation(.easeOut(duration: 0.24))`, transitions such as\n  `.opacity.combined(with: .move(edge: .bottom))`, and\n  `@Environment(\\.accessibilityReduceMotion)` to swap movement for a fade.\n- Jetpack Compose: `animate*AsState`, `AnimatedVisibility` with `fadeIn(\n  tween(240))`, `updateTransition`. Compose follows the system's animator\n  duration scale, including \"Remove animations\"; a custom loop reads that\n  setting itself.\n\n## Check it\n\n- Search the code for `transition: all`, `transition-all`, `animate-bounce`\n  and `animate-ping` used as decoration, `motion.` components without\n  `LazyMotion`, and `setState` inside animation loops.\n- `preview` with `motion: true` after building: the timeline, the\n  animations driven from JavaScript, and the reduced-motion pass.\n- The bundle before and after, from the build's size report: the motion of\n  a page should cost a few kilobytes, not a library.\n\n## Avoid\n\nA motion library for fades; `motion.div` without `LazyMotion`; `initial`\nhiding server-rendered text; `transition-all`; `animate-bounce` and\n`animate-ping` as decoration; `setState` every frame; reduced motion\nhandled in each component instead of once; web-style transitions in a\nnative app where the platform has its own.\n","# Motion in each stack\n\nThe rules are the same in every stack (`motion`); this is how each one\nwrites them. Whatever the stack: the tokens are defined once, reduced\nmotion is handled once at the root rather than in every component, only\nopacity and transform move, and no library is added for what CSS already\ndoes (`motion-performance`).\n\n## 1. Plain CSS and JavaScript\n\n- The tokens, in one place:\n\n```css\n:root {\n  --m-ease-out: cubic-bezier(0.22, 0.61, 0.36, 1);\n  --m-ease-in: cubic-bezier(0.55, 0.055, 0.675, 0.19);\n  --m-ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);\n  --m-instant: 100ms;\n  --m-fast: 160ms;\n  --m-base: 240ms;\n  --m-panel: 320ms;\n  --m-enter: 700ms;\n  --m-draw: 1000ms;\n  --m-shift-sm: 6px;\n  --m-shift: 12px;\n  --m-shift-lg: 18px;\n}\n```\n\n- Keyframes named for what they do (`rise`, `fade`, `draw`, `count`), with\n  only a `from` when they end at the element's own style.\n- Movement written inside `@media (prefers-reduced-motion: no-preference)`\n  rather than undone inside `reduce`: a rule added later is then safe by\n  default.\n- The reveal module from `motion-reveal`, started once:\n  `startReveal(document.documentElement)` at the end of `\u003Cbody>` or on\n  `DOMContentLoaded`.\n- Elements shown and hidden with `display` or `[open]` (dialogs, popovers)\n  animate in and out with `@starting-style` and `allow-discrete`\n  transitions (`motion-interface`).\n- One-off motion from script uses the Web Animations API, not a\n  `requestAnimationFrame` loop writing styles:\n  `el.animate([{ opacity: 0 }, { opacity: 1 }], { duration: 240, easing:\n  \"cubic-bezier(0.22, 0.61, 0.36, 1)\" })`. It runs like CSS and returns an\n  animation you can await (`.finished`), `cancel()` or `reverse()`.\n- Replaying CSS animations (a drawing, on hover): give the parts\n  `animation-fill-mode: both`, then for each of\n  `el.getAnimations({ subtree: true })` set `currentTime = 0` and call\n  `play()`. A finished animation with `backwards` fill is no longer in\n  effect, so `getAnimations()` does not return it: replayable parts need\n  `both`.\n- Reduced motion in script: `matchMedia(\"(prefers-reduced-motion:\n  reduce)\")`, and its `change` event for anything that keeps running.\n\n## 2. Tailwind CSS\n\nVersion 4 keeps the tokens in CSS:\n\n```css\n@theme {\n  --ease-soft-out: cubic-bezier(0.22, 0.61, 0.36, 1);\n  --ease-soft-in-out: cubic-bezier(0.65, 0, 0.35, 1);\n  --animate-rise: rise 700ms var(--ease-soft-out) backwards;\n  --animate-fade: fade 600ms var(--ease-soft-out) backwards;\n  @keyframes rise {\n    from { opacity: 0; transform: translateY(12px); }\n  }\n  @keyframes fade {\n    from { opacity: 0; }\n  }\n}\n```\n\n- That gives `ease-soft-out`, `animate-rise` and `animate-fade` as\n  utilities: `transition-colors duration-150 ease-soft-out`,\n  `motion-safe:animate-rise`.\n- `motion-safe:` on every animation and every transform transition;\n  `motion-reduce:transition-none` or `motion-reduce:animate-none` where a\n  component library adds its own.\n- A revealed block's children: `group` on the block, then\n  `motion-safe:group-data-[shown]:animate-rise` on the child. The hidden\n  pre-state and the arrival delay are clearer as the dozen lines of CSS in\n  `motion-reveal` in the global stylesheet than as long utility strings on\n  every element.\n- Version 3: the same tokens in `theme.extend` (`transitionTimingFunction`,\n  `keyframes`, `animation`) in `tailwind.config`; `motion-safe:` and\n  `motion-reduce:` are the same.\n- shadcn\u002Fui's overlays use `tw-animate-css` (formerly\n  `tailwindcss-animate`): `data-[state=open]:animate-in fade-in-0\n  zoom-in-95`, `data-[state=closed]:animate-out fade-out-0 zoom-out-95`,\n  with `duration-200`. Keep the distances small (`slide-in-from-top-2`) and\n  leave its defaults when they already match `motion-interface`.\n\n## 3. React\n\n- The reveal hook sets the flag before paint and starts the one observer:\n\n```tsx\nexport function useReveal(page: RefObject\u003CHTMLElement | null>) {\n  useLayoutEffect(() => {\n    const root = page.current;\n    return root ? startReveal(root) : undefined; \u002F\u002F motion-reveal, section 2\n  }, [page]);\n}\n```\n\n  `useLayoutEffect`, not `useEffect`, for anything that sets a hidden\n  pre-state: it runs before the browser paints.\n- Per-frame values never pass through the state of a large tree. A clock\n  that ticks ten times a second (a scripted demo, `motion-demo`) lives in\n  the small component that shows it; a parent that needs to follow along\n  changes state only when something it shows changes (a phase, a few times\n  per loop), through a callback in an effect.\n- Continuous values (the pointer, a drag, a progress) go into refs or CSS\n  variables on an element (`el.style.setProperty(\"--x\", ...)`), never into\n  `setState` sixty times a second.\n- Replay from an event handler with `getAnimations()` on a ref.\n- Changing a `key` remounts and replays mount animations: use it on\n  purpose, never by accident in a list.\n- Strict mode runs effects twice in development: every observer\n  disconnects in its cleanup.\n\n### The Motion library (`motion`, formerly Framer Motion)\n\n- It earns its weight for: exits of components React removes\n  (`AnimatePresence`), layout animation (an element changing size or place\n  smoothly, `layout`), shared elements between views (`layoutId`), and\n  gestures with springs (drag with momentum). Not for fades and reveals\n  that CSS does.\n- Size: import from `motion\u002Freact`, wrap the app in `LazyMotion` with\n  `domAnimation` (`domMax` only for layout and drag) and use `m.div`, not\n  `motion.div`. That keeps it to a fraction of the full component's size.\n- Reduced motion once, at the root: `\u003CMotionConfig reducedMotion=\"user\">`\n  turns off transform and layout animation and keeps opacity;\n  `useReducedMotion()` for choices in code.\n- Reveals: `whileInView` with `viewport={{ once: true, margin: \"0px 0px\n  -10% 0px\" }}` and `initial={{ opacity: 0, y: 12 }}`. The `initial` state\n  is rendered on the server, so that content is hidden until the page\n  hydrates: give content above the fold `initial={false}`, and accept that\n  hidden text below it is invisible without JavaScript, or use the CSS\n  reveal instead.\n- Exits: `\u003CAnimatePresence>` around conditionally rendered children, with\n  `exit={{ opacity: 0 }}`; `mode=\"wait\"` when one view replaces another.\n- Timing from the tokens, kept in one object that mirrors the CSS:\n  `transition={{ duration: 0.24, ease: [0.22, 0.61, 0.36, 1] }}`. Springs\n  for drags and layout, critically damped so nothing overshoots:\n  `{ type: \"spring\", stiffness: 400, damping: 40 }`.\n\n### Other libraries\n\n- GSAP for a long scripted timeline with many parts, with\n  `gsap.matchMedia()` for reduced motion; ScrollTrigger only when CSS\n  scroll-driven animations cannot do the job.\n- Lottie for a designer-made animation, with the lighter dotLottie player\n  rather than the full `lottie-web`, and the first or last frame shown\n  under reduced motion.\n- Each is a cost you state in the report (`motion-performance`).\n\n## 4. Next.js\n\n- Motion lives in client components; keep the `\"use client\"` boundary to\n  the animated part (a `Reveal` wrapper, the demo), not the whole page.\n- Server-rendered markup paints before hydration. A hidden pre-state needs\n  the inline head script with its safety timeout (`motion-reveal`,\n  section 3), written into `\u003Chead>` in `app\u002Flayout.tsx` with\n  `dangerouslySetInnerHTML`, and `suppressHydrationWarning` on `\u003Chtml>`,\n  since the attribute differs from the server's.\n- Route transitions: the View Transitions API where the version supports\n  it (recent versions have an experimental view transitions flag used with\n  React's `ViewTransition` component), or `document.startViewTransition`\n  around the navigation. No fade on every route.\n- `next\u002Ffont` so a font swap does not move text in the middle of an\n  entrance; `next\u002Fimage` with sizes so an image does not shift a block\n  while it reveals.\n- `loading.tsx` shows only when the wait is worth showing\n  (`motion-interface`, loading).\n\n## 5. Vue\n\n- `\u003CTransition name=\"fade\">` with:\n\n```css\n.fade-enter-active,\n.fade-leave-active { transition: opacity var(--m-base) var(--m-ease-out); }\n.fade-enter-from,\n.fade-leave-to { opacity: 0; }\n```\n\n  and `appear` to play on the first render.\n- `\u003CTransitionGroup tag=\"ul\" name=\"list\">` for lists, with `.list-move {\n  transition: transform var(--m-base) var(--m-ease-out); }` for reordering\n  and `.list-leave-active { position: absolute; }` so the others close the\n  gap.\n- Reduced motion: `usePreferredReducedMotion()` from VueUse, with a `none`\n  transition name when it is `reduce`, or the transition classes inside the\n  no-preference media query.\n- A `v-reveal` directive that adds its element to the page's one observer.\n- Nuxt: short `pageTransition` and `layoutTransition`; the reveal flag from\n  an inline head script through `useHead`.\n\n## 6. Svelte (5)\n\n- `transition:fade={{ duration: 240 }}`, `in:fly={{ y: 12, duration: 240,\n  easing: cubicOut }}`, `out:fade={{ duration: 160 }}`, and\n  `animate:flip={{ duration: 240 }}` inside a keyed `{#each}` for\n  reordering.\n- Custom transitions return `css: (t) => ...`, so they run as CSS rather\n  than per-frame JavaScript.\n- Reduced motion: `import { prefersReducedMotion } from \"svelte\u002Fmotion\"`,\n  then `duration: prefersReducedMotion.current ? 0 : 240`.\n- `Tween` and `Spring` from `svelte\u002Fmotion` for values that animate (a\n  number, a position): `new Tween(0, { duration: 400, easing: cubicOut })`.\n- `{#key value}` replays intro transitions when the value changes: only on\n  purpose.\n- SvelteKit: `onNavigate` with `document.startViewTransition` for route\n  transitions.\n\n## 7. Native mobile, briefly\n\n- The platform's own navigation and sheet transitions win over web habits.\n- React Native: Reanimated, which runs on the UI thread. `withTiming(value,\n  { duration: 240, easing: Easing.bezier(0.22, 0.61, 0.36, 1),\n  reduceMotion: ReduceMotion.System })`; `useReducedMotion()` for choices;\n  layout animations such as\n  `entering={FadeIn.duration(240).reduceMotion(ReduceMotion.System)}`.\n- Flutter: implicit animations (`AnimatedOpacity`, `AnimatedContainer`,\n  `AnimatedSwitcher`) for changes of state, `Curves.easeOutCubic` close to\n  the tokens, and `MediaQuery.disableAnimationsOf(context)` to set\n  durations to zero.\n- SwiftUI: `withAnimation(.easeOut(duration: 0.24))`, transitions such as\n  `.opacity.combined(with: .move(edge: .bottom))`, and\n  `@Environment(\\.accessibilityReduceMotion)` to swap movement for a fade.\n- Jetpack Compose: `animate*AsState`, `AnimatedVisibility` with `fadeIn(\n  tween(240))`, `updateTransition`. Compose follows the system's animator\n  duration scale, including \"Remove animations\"; a custom loop reads that\n  setting itself.\n\n## Check it\n\n- Search the code for `transition: all`, `transition-all`, `animate-bounce`\n  and `animate-ping` used as decoration, `motion.` components without\n  `LazyMotion`, and `setState` inside animation loops.\n- `preview` with `motion: true` after building: the timeline, the\n  animations driven from JavaScript, and the reduced-motion pass.\n- The bundle before and after, from the build's size report: the motion of\n  a page should cost a few kilobytes, not a library.\n\n## Avoid\n\nA motion library for fades; `motion.div` without `LazyMotion`; `initial`\nhiding server-rendered text; `transition-all`; `animate-bounce` and\n`animate-ping` as decoration; `setState` every frame; reduced motion\nhandled in each component instead of once; web-style transitions in a\nnative app where the platform has its own.\n",{"name":5,"description":7},"main","38293fe1b096e6bf3646c7344c38786ed688f464","crates\u002Fenowx-core\u002Fskills\u002Fmotion-stacks",21,"https:\u002F\u002Fgithub.com\u002Fenowdev\u002Fenowxcli","https:\u002F\u002Fenowx.ai","https:\u002F\u002Fgithub.com\u002Fenowdev","Pretending to be a Developer","2026-10-06 02:05:35",{"source":8,"skillName":5,"commands":64,"prompt":74,"zipAvailable":75},[65,68,71],{"tool":66,"command":67},"npx","npx skills add enowdev\u002Fenowxcli --skill motion-stacks",{"tool":69,"command":70},"bunx","bunx skills add enowdev\u002Fenowxcli --skill motion-stacks",{"tool":72,"command":73},"pnpm","pnpm dlx skills add enowdev\u002Fenowxcli --skill motion-stacks","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fenowdev\u002Fenowxcli\u002Fblob\u002Fmain\u002Fcrates\u002Fenowx-core\u002Fskills\u002Fmotion-stacks\u002FSKILL.md",true,[],[78,91,113,129,153,175],{"skillNo":79,"name":80,"nameZh":6,"description":81,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":82,"categories":83,"keywords":87,"publishedAt":49},"01M6D2HQYSQD9N7MTNDT4A7CBM","demo-skill","Project-scope demo skill for tests.","https:\u002F\u002Fgithub.com\u002Fenowdev\u002Fenowxcli\u002Fblob\u002Fmain\u002Fworkspace\u002F.agents\u002Fskills\u002Fdemo-skill\u002FSKILL.md",[84],{"categoryNo":85,"categoryName":86,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",[88,89,90,33,36],"demo skill","demo","tests",{"skillNo":92,"name":39,"nameZh":6,"description":93,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":94,"categories":95,"keywords":102,"publishedAt":49},"01M6D2HM0GCPM0TM2HPG9HHR2R","Writing words people read: the specific thing instead of claims, words to drop, sentences, interface copy (buttons, labels, errors, empty states, confirmations), page copy, notifications and emails, alt text and link text, numbers and dates, words that translate well, documentation and voice. Read before writing a page's copy, an interface's text or a document.","https:\u002F\u002Fgithub.com\u002Fenowdev\u002Fenowxcli\u002Fblob\u002Fmain\u002Fcrates\u002Fenowx-core\u002Fskills\u002Fwriting\u002FSKILL.md",[96,99],{"categoryNo":97,"categoryName":98,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000018","写作与文案",{"categoryNo":100,"categoryName":101,"taxonomyType":26,"taxonomyTypeDesc":27},"01JBSEEDCAT000000000000036","内容创作者",[39,103,104,105,106,107,36,108,109,110,111,112],"text","emails","empty","drop","document","page","voice","notifications","documentation","translate",{"skillNo":114,"name":115,"nameZh":6,"description":116,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":117,"categories":118,"keywords":122,"publishedAt":49},"01M6D2HG07YS49NM196Q0R3AZ8","ui","Designing or restyling an interface: direction, layout, type, colour, icons, components, states, responsive behaviour. Read before building a page, a screen or a set of components.","https:\u002F\u002Fgithub.com\u002Fenowdev\u002Fenowxcli\u002Fblob\u002Fmain\u002Fcrates\u002Fenowx-core\u002Fskills\u002Fui\u002FSKILL.md",[119],{"categoryNo":120,"categoryName":121,"taxonomyType":26,"taxonomyTypeDesc":27},"01JBSEEDCAT000000000000032","设计师",[115,123,124,125,126,108,127,36,128],"icons","components","SET","responsive","layout","before building",{"skillNo":130,"name":131,"nameZh":6,"description":132,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":133,"categories":134,"keywords":139,"publishedAt":49},"01M6D2HB59HZHWG1RYZDV4SJ91","ui-themes","Light and dark themes, and colour that holds in both: tokens with a value per theme, and what each kind of content needs to stay visible (icons, SVG drawings and line art, logos, photos, screenshots, charts, code, maps, borders, shadows, focus, form controls), switching without a flash, and checking both. Read before building a second theme or a theme toggle, and before any drawing, logo or chart on a page that has two themes.","https:\u002F\u002Fgithub.com\u002Fenowdev\u002Fenowxcli\u002Fblob\u002Fmain\u002Fcrates\u002Fenowx-core\u002Fskills\u002Fui-themes\u002FSKILL.md",[135,138],{"categoryNo":136,"categoryName":137,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000015","视觉设计",{"categoryNo":24,"categoryName":25,"taxonomyType":26,"taxonomyTypeDesc":27},[115,140,141,142,143,144,145,146,147,36,128,148,108,45,149,150,123,151,152],"ui themes","switching","photos","tokens","drawing","focus","maps","Art","flash","Charts","content","screenshots","logo",{"skillNo":154,"name":155,"nameZh":6,"description":156,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":157,"categories":158,"keywords":162,"publishedAt":49},"01M6D2H5R5KRAE524C8QX0T7Q0","ui-structure","Structure before styling: the blueprint written before any interface code (the screen's job, the regions its kind of page needs, their order and why, the layout skeleton in numbers, the components and their states), the anatomy and order of a homepage, a dashboard, a list, a detail page, settings, a form, sign-in, pricing, docs, an article and a checkout, and how to turn the blueprint into code (tokens, layout primitives, components, sections, page). Read first, before building or rebuilding any page or screen.","https:\u002F\u002Fgithub.com\u002Fenowdev\u002Fenowxcli\u002Fblob\u002Fmain\u002Fcrates\u002Fenowx-core\u002Fskills\u002Fui-structure\u002FSKILL.md",[159,160,161],{"categoryNo":97,"categoryName":98,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":24,"categoryName":25,"taxonomyType":26,"taxonomyTypeDesc":27},{"categoryNo":100,"categoryName":101,"taxonomyType":26,"taxonomyTypeDesc":27},[163,115,164,124,165,166,45,167,168,169,127,143,170,171,172,36,128,108,173,174],"structure","ui structure","pricing","dashboard","Blueprint","article","anatomy","styling","primitives","list","why","how",{"skillNo":176,"name":177,"nameZh":6,"description":178,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":179,"categories":180,"keywords":186,"publishedAt":49},"01M6D2H0ZZBJG77MBGPZHA6MWD","ui-stack-vue","Building interfaces in Vue 3 and Nuxt: project layout, script setup with typed defineProps, defineEmits and defineModel, attribute fallthrough, slots and scoped slots, template refs, ref, reactive and computed, watch versus watchEffect, composables and VueUse, provide and inject, Pinia, keyed v-for, transitions, Nuxt 4 (app directory, pages, layouts, useFetch and useAsyncData keys, $fetch in handlers, server routes, runtime config, useSeoMeta, NuxtImg, middleware), VeeValidate forms, Reka UI and Nuxt UI, testing, vue-tsc, and the traps. Read when the project uses Vue or Nuxt.","https:\u002F\u002Fgithub.com\u002Fenowdev\u002Fenowxcli\u002Fblob\u002Fmain\u002Fcrates\u002Fenowx-core\u002Fskills\u002Fui-stack-vue\u002FSKILL.md",[181,184,185],{"categoryNo":182,"categoryName":183,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量",{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},{"categoryNo":120,"categoryName":121,"taxonomyType":26,"taxonomyTypeDesc":27},[115,43,38,187,188,189,190,191,127,33,192,193,36,194,195,196,197,198,199,200],"ui stack vue","forms","pages","watch","runtime","transitions","fetch","pinia","nuxt","template","server","config","directory","testing"]