{"_id":"@aphrody/m3-motion","_rev":"4-b8ec66d98138838deae9d3cf1681c65f","name":"@aphrody/m3-motion","dist-tags":{"latest":"3.2.5"},"versions":{"3.2.1":{"name":"@aphrody/m3-motion","version":"3.2.1","author":"aphrody-code","license":"Apache-2.0","_id":"@aphrody/m3-motion@3.2.1","maintainers":[{"name":"yohan971","email":"yohanpierre@live.fr"}],"homepage":"https://github.com/aphrody-code/material-web/tree/main/packages/m3-motion#readme","bugs":{"url":"https://github.com/aphrody-code/material-web/issues"},"dist":{"shasum":"72cb16032347b29d9dbfba92789bb03a0603ec40","tarball":"https://registry.npmjs.org/@aphrody/m3-motion/-/m3-motion-3.2.1.tgz","fileCount":28,"integrity":"sha512-nspuTg+wtz09smGR1iQh4iuQUoVeezzFlVGwsoMILG8oqflfhHCs2XzNtVDdJjcq1dBKoJpgcnzg7b72RnHjxQ==","signatures":[{"sig":"MEUCIQCjMI4FCbtTCtrgoFlFqV6BAa0mtQPq1FTOF4Hb7zPMeQIgYJWg60MhmyRWis4jiBl+xu2O7qW1VQGyc2InUuV/sYE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":132813},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","shasum":"72cb16032347b29d9dbfba92789bb03a0603ec40","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"scripts":{"test":"bun test","build":"tsup && tsc -p tsconfig.build.json","typecheck":"tsc --noEmit","typecheck:fast":"tsgo --noEmit"},"_npmUser":{"name":"yohan971","email":"yohanpierre@live.fr"},"_integrity":"sha512-nspuTg+wtz09smGR1iQh4iuQUoVeezzFlVGwsoMILG8oqflfhHCs2XzNtVDdJjcq1dBKoJpgcnzg7b72RnHjxQ==","repository":{"url":"git+https://github.com/aphrody-code/material-web.git","type":"git","directory":"packages/m3-motion"},"_npmVersion":"10.8.3","description":"Material 3 animation plugin powered by Motion (formerly Framer Motion)","directories":{},"_nodeVersion":"24.3.0","dependencies":{"motion":"^12.40.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.1","react":"^19.2.6","typescript":"^6.0.3","@types/react":"^19.2.15"},"peerDependencies":{"react":">=18.0.0","motion":">=12.0.0"},"_npmOperationalInternal":{"tmp":"tmp/m3-motion_3.2.1_1780527333608_0.8596302420760795","host":"s3://npm-registry-packages-npm-production"}},"3.2.2":{"name":"@aphrody/m3-motion","version":"3.2.2","author":"aphrody-code","license":"Apache-2.0","_id":"@aphrody/m3-motion@3.2.2","maintainers":[{"name":"yohan971","email":"yohanpierre@live.fr"}],"homepage":"https://github.com/aphrody-code/material-web/tree/main/packages/m3-motion#readme","bugs":{"url":"https://github.com/aphrody-code/material-web/issues"},"dist":{"shasum":"e6e6e48327b47d4d628e441afbeab283411ead45","tarball":"https://registry.npmjs.org/@aphrody/m3-motion/-/m3-motion-3.2.2.tgz","fileCount":28,"integrity":"sha512-zl/WLeRi8yMuIqK02diRXjOjnqwRFSwsNEFbRxH/T6nYrxgWAYTFq3fDliZjiLxgwi8uIuDqE9aWX7uaTBXiGg==","signatures":[{"sig":"MEUCIHyUch5DUzwHfqnfxpIXml7CUynW/70EswCrzjTS5eiGAiEA1j1dpSXarIMtaLR8DOHEjoy+ro+qVddFqkK7Y9zIN00=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":132813},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","shasum":"e6e6e48327b47d4d628e441afbeab283411ead45","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"scripts":{"test":"bun test","build":"tsup && tsc -p tsconfig.build.json","typecheck":"tsc --noEmit","typecheck:fast":"tsgo --noEmit"},"_npmUser":{"name":"yohan971","email":"yohanpierre@live.fr"},"_integrity":"sha512-zl/WLeRi8yMuIqK02diRXjOjnqwRFSwsNEFbRxH/T6nYrxgWAYTFq3fDliZjiLxgwi8uIuDqE9aWX7uaTBXiGg==","repository":{"url":"git+https://github.com/aphrody-code/material-web.git","type":"git","directory":"packages/m3-motion"},"_npmVersion":"10.8.3","description":"Material 3 animation plugin powered by Motion (formerly Framer Motion)","directories":{},"_nodeVersion":"24.3.0","dependencies":{"motion":"^12.40.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.1","react":"^19.2.6","typescript":"^6.0.3","@types/react":"^19.2.15"},"peerDependencies":{"react":">=18.0.0","motion":">=12.0.0"},"_npmOperationalInternal":{"tmp":"tmp/m3-motion_3.2.2_1780528053547_0.6721022036300348","host":"s3://npm-registry-packages-npm-production"}},"3.2.4":{"name":"@aphrody/m3-motion","version":"3.2.4","author":"aphrody-code","license":"Apache-2.0","_id":"@aphrody/m3-motion@3.2.4","maintainers":[{"name":"yohan971","email":"yohanpierre@live.fr"}],"homepage":"https://github.com/aphrody-code/material-web/tree/main/packages/m3-motion#readme","bugs":{"url":"https://github.com/aphrody-code/material-web/issues"},"dist":{"shasum":"ae48dc64c9ab251e3b6fb16d6543d869a7957d1b","tarball":"https://registry.npmjs.org/@aphrody/m3-motion/-/m3-motion-3.2.4.tgz","fileCount":28,"integrity":"sha512-XtHicxDnxcoXjwwp84QsDm+48bjtsxuZbHex8KbfYtf9U31x+sLPM/rK46e+CgLcrlSWl2DOumYUz/zUnRxK9g==","signatures":[{"sig":"MEUCIFwDCGSubhL5hq7yH+wAvdQ8NMjF+zE5YQRe6T/Hj+AFAiEA+j9ddKHhYNI2KRUXtSD1lS5+Rx2AabwxHbJkVYE5Zog=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":132813},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","shasum":"ae48dc64c9ab251e3b6fb16d6543d869a7957d1b","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"scripts":{"test":"bun test","build":"tsup && tsc -p tsconfig.build.json","typecheck":"tsc --noEmit","typecheck:fast":"tsgo --noEmit"},"_npmUser":{"name":"yohan971","email":"yohanpierre@live.fr"},"_integrity":"sha512-XtHicxDnxcoXjwwp84QsDm+48bjtsxuZbHex8KbfYtf9U31x+sLPM/rK46e+CgLcrlSWl2DOumYUz/zUnRxK9g==","repository":{"url":"git+https://github.com/aphrody-code/material-web.git","type":"git","directory":"packages/m3-motion"},"_npmVersion":"10.8.3","description":"Material 3 animation plugin powered by Motion (formerly Framer Motion)","directories":{},"_nodeVersion":"24.3.0","dependencies":{"motion":"^12.40.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.1","react":"^19.2.6","typescript":"^6.0.3","@types/react":"^19.2.15"},"peerDependencies":{"react":">=18.0.0","motion":">=12.0.0"},"_npmOperationalInternal":{"tmp":"tmp/m3-motion_3.2.4_1786525661630_0.1859036622417014","host":"s3://npm-registry-packages-npm-production"}},"3.2.5":{"name":"@aphrody/m3-motion","version":"3.2.5","description":"Material 3 animation plugin powered by Motion (formerly Framer Motion)","homepage":"https://github.com/aphrody-code/material-web/tree/main/packages/m3-motion#readme","bugs":{"url":"https://github.com/aphrody-code/material-web/issues"},"license":"Apache-2.0","author":{"name":"aphrody-code"},"repository":{"type":"git","url":"git+https://github.com/aphrody-code/material-web.git","directory":"packages/m3-motion"},"type":"module","main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"}},"publishConfig":{"access":"public"},"scripts":{"build":"tsup && tsc -p tsconfig.build.json","typecheck":"tsc --noEmit","typecheck:fast":"tsgo --noEmit","test":"bun test"},"dependencies":{"motion":"catalog:"},"devDependencies":{"@types/react":"catalog:","react":"catalog:","tsup":"catalog:","typescript":"catalog:"},"peerDependencies":{"motion":">=12.0.0","react":">=18.0.0"},"gitHead":"6981e124848acb9ce5364d87af955fd5fe992e23","_id":"@aphrody/m3-motion@3.2.5","_nodeVersion":"24.3.0","_npmVersion":"9.2.0","dist":{"integrity":"sha512-qM7ruLLuz3q9CHk8myD8b3/N4F2JueMavR2pb1bw6uSNdR5+PwY4ktjFTN/6xRPkyTH7ltfwpVp/wRSH3vBYIg==","shasum":"cb98380b3d1e46d4cb116dd0696d4f106a3ad908","tarball":"https://registry.npmjs.org/@aphrody/m3-motion/-/m3-motion-3.2.5.tgz","fileCount":2,"unpackedSize":9331,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCJp1sAYFBw/tNy5Pr/19RJDV8E+CEe+nublxXAZu16GwIgW5VnCBrXAhgHW4tkY6NroZtxwtcLjxLiuqjgum6ywtk="}]},"_npmUser":{"name":"yohan971","email":"yohanpierre@live.fr"},"directories":{},"maintainers":[{"name":"yohan971","email":"yohanpierre@live.fr"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/m3-motion_3.2.5_1789022179298_0.30261294584634224"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-03T22:55:33.381Z","modified":"2026-09-10T06:36:19.643Z","3.2.1":"2026-06-03T22:55:33.759Z","3.2.2":"2026-06-03T23:07:33.719Z","3.2.4":"2026-08-12T09:07:41.750Z","3.2.5":"2026-09-10T06:36:19.430Z"},"bugs":{"url":"https://github.com/aphrody-code/material-web/issues"},"author":{"name":"aphrody-code"},"license":"Apache-2.0","homepage":"https://github.com/aphrody-code/material-web/tree/main/packages/m3-motion#readme","repository":{"type":"git","url":"git+https://github.com/aphrody-code/material-web.git","directory":"packages/m3-motion"},"description":"Material 3 animation plugin powered by Motion (formerly Framer Motion)","maintainers":[{"name":"yohan971","email":"yohanpierre@live.fr"}],"readme":"# @aphrody/m3-motion\n\nHigh-fidelity React animation components for Material Design 3 (M3) motion specifications, powered by [Motion](https://motion.dev/) (formerly Framer Motion).\n\nThis package implements the core Material Design 3 motion patterns—including **Container Transform**, **Shared Axis**, **Fade Through**, **Collapse**, and **Fade**—using Google's official spring, easing, and duration tokens.\n\n---\n\n## Installation\n\n```bash\nbun add @aphrody/m3-motion motion\n```\n\nEnsure you have `react` (>= 18.0.0) and `motion` (>= 12.0.0) installed in your project.\n\n---\n\n## 1. Container Transform (`M3ContainerTransform`)\n\nThe container transform pattern transitions one UI element to another (e.g., morphing a small Card component into a full-page Detail view). \n\n### Key Features\n* **Aspect Ratio Preservation**: Automatically prevents layout distortion (skewing/stretching) of children during container morphs using layout-position correction.\n* **Spec-Compliant Cross-Fade**: Fades out the starting content early (0% to 30% of transition) and delays the fade-in of target content (20% to 100% of transition) to avoid messy intermediate states.\n* **Pointer Event Management**: Prevents inactive, hidden content from intercepting pointer interactions.\n* **Flexibility**: Supports both the **Single-Container Toggle** approach (re-rendering within the same container) and the **Two-Container Shared Layout** approach (different source/target elements linked via `layoutId`).\n\n### Sub-Components\n* `M3ContainerTransform`: Parent morphing container.\n* `M3ContainerTransformStart`: Wrapper for starting content (e.g., collapsed card details).\n* `M3ContainerTransformEnd`: Wrapper for ending content (e.g., expanded page details).\n\n---\n\n### Code Examples\n\n#### Single-Container Toggle Approach\nBest when morphing a single component instance in place using state.\n\n```tsx\nimport React, { useState } from \"react\";\nimport { \n  M3ContainerTransform, \n  M3ContainerTransformStart, \n  M3ContainerTransformEnd \n} from \"@aphrody/m3-motion\";\n\nexport const MorphingCard = () => {\n  const [isExpanded, setIsExpanded] = useState(false);\n\n  return (\n    <M3ContainerTransform\n      isExpanded={isExpanded}\n      speed=\"default\"\n      style={{\n        width: isExpanded ? 500 : 250,\n        height: isExpanded ? 400 : 150,\n        borderRadius: isExpanded ? 28 : 16,\n        backgroundColor: \"var(--md-sys-color-surface-container)\",\n        cursor: \"pointer\"\n      }}\n      onClick={() => setIsExpanded(!isExpanded)}\n    >\n      {/* Start state (collapsed content) */}\n      <M3ContainerTransformStart>\n        <div style={{ padding: 16 }}>\n          <h3>Collapsed Card</h3>\n          <p>Click me to expand...</p>\n        </div>\n      </M3ContainerTransformStart>\n\n      {/* End state (expanded detail content) */}\n      <M3ContainerTransformEnd>\n        <div style={{ padding: 24 }}>\n          <h2>Detailed View</h2>\n          <p>This is the expanded content showing the full detail surface.</p>\n          <button onClick={(e) => { e.stopPropagation(); setIsExpanded(false); }}>\n            Close\n          </button>\n        </div>\n      </M3ContainerTransformEnd>\n    </M3ContainerTransform>\n  );\n};\n```\n\n#### Two-Container Shared Layout Approach (linked via `layoutId`)\nBest for page/route transitions or separate DOM elements. Wrap the conditional render in `<AnimatePresence>` to enable the exit fade-out transition.\n\n```tsx\nimport React, { useState } from \"react\";\nimport { AnimatePresence } from \"motion/react\";\nimport { \n  M3ContainerTransform, \n  M3ContainerTransformStart, \n  M3ContainerTransformEnd \n} from \"@aphrody/m3-motion\";\n\nexport const ListView = () => {\n  const [selectedId, setSelectedId] = useState<string | null>(null);\n\n  return (\n    <div>\n      {/* List of items */}\n      {!selectedId && (\n        <div className=\"grid\">\n          <M3ContainerTransform \n            layoutId=\"card-item-1\" \n            onClick={() => setSelectedId(\"1\")}\n            style={{ borderRadius: 16, background: \"#eee\", width: 200, height: 120 }}\n          >\n            <M3ContainerTransformStart>\n              <div style={{ padding: 12 }}>Card Content 1</div>\n            </M3ContainerTransformStart>\n          </M3ContainerTransform>\n        </div>\n      )}\n\n      {/* Expanded detail page */}\n      <AnimatePresence>\n        {selectedId === \"1\" && (\n          <div className=\"backdrop\">\n            <M3ContainerTransform \n              layoutId=\"card-item-1\" \n              style={{ borderRadius: 24, background: \"#fff\", width: 600, height: 450 }}\n            >\n              <M3ContainerTransformEnd>\n                <div style={{ padding: 24 }}>\n                  <h2>Expanded Title 1</h2>\n                  <p>Long detail text...</p>\n                  <button onClick={() => setSelectedId(null)}>Back to List</button>\n                </div>\n              </M3ContainerTransformEnd>\n            </M3ContainerTransform>\n          </div>\n        )}\n      </AnimatePresence>\n    </div>\n  );\n};\n```\n\n---\n\n## 2. Shared Axis (`M3SharedAxis`)\n\nUsed for transitions between UI elements that have a spatial or navigational relationship (e.g., page stepper, tab switching, or dialog overlays). Supports `x`, `y`, and `z` axes.\n\n* Default enter duration: **300ms** (`medium2`) with `emphasizedDecelerate` ease.\n* Default exit duration: **200ms** (`short4`) with `emphasizedAccelerate` ease.\n\n### Code Example\n\n```tsx\nimport React, { useState } from \"react\";\nimport { M3SharedAxis } from \"@aphrody/m3-motion\";\n\nexport const StepWizard = () => {\n  const [step, setStep] = useState(0);\n\n  return (\n    <div>\n      <M3SharedAxis \n        stateKey={step} \n        axis=\"x\" \n        forward={step > 0} \n        style={{ padding: 20 }}\n      >\n        {step === 0 && <div>Step 1: Enter details</div>}\n        {step === 1 && <div>Step 2: Review options</div>}\n        {step === 2 && <div>Step 3: Confirm purchase</div>}\n      </M3SharedAxis>\n      \n      <button onClick={() => setStep(prev => Math.max(0, prev - 1))}>Back</button>\n      <button onClick={() => setStep(prev => Math.min(2, prev + 1))}>Next</button>\n    </div>\n  );\n};\n```\n\n---\n\n## 3. Fade Through (`M3FadeThrough`)\n\nUsed for transitions between primary views that do not have a strong spatial relationship (e.g., bottom navigation switching, settings screens).\n\n* Default exit duration (fade-out): **90ms** with `emphasizedAccelerate` ease.\n* Default enter duration (fade-in & scale-up from 92%): **210ms** with `emphasizedDecelerate` ease.\n\n### Code Example\n\n```tsx\nimport React, { useState } from \"react\";\nimport { M3FadeThrough } from \"@aphrody/m3-motion\";\n\nexport const AppNavigation = () => {\n  const [currentTab, setCurrentTab] = useState(\"home\");\n\n  return (\n    <div>\n      <M3FadeThrough stateKey={currentTab} style={{ height: 400 }}>\n        {currentTab === \"home\" && <div>Home View Content</div>}\n        {currentTab === \"search\" && <div>Search Page Content</div>}\n        {currentTab === \"profile\" && <div>User Profile Settings</div>}\n      </M3FadeThrough>\n\n      <nav>\n        <button onClick={() => setCurrentTab(\"home\")}>Home</button>\n        <button onClick={() => setCurrentTab(\"search\")}>Search</button>\n        <button onClick={() => setCurrentTab(\"profile\")}>Profile</button>\n      </nav>\n    </div>\n  );\n};\n```\n\n---\n\n## 4. Duration and Easing Presets\n\nDevelopers have direct access to official Material Design 3 timings and curves:\n\n```tsx\nimport { m3Easings, m3Durations, m3Springs } from \"@aphrody/m3-motion\";\n\n// Custom animation example using M3 tokens\nconst myCustomTransition = {\n  ease: m3Easings.emphasizedDecelerate,\n  duration: m3Durations.medium4, // 400ms\n};\n```\n\n### Motion Presets Summary\n* **`m3Easings`**: Bezier curves for `emphasized` (approx), `emphasizedDecelerate` (enter), `emphasizedAccelerate` (exit), `standard`, `standardDecelerate`, and `standardAccelerate`.\n* **`m3Durations`**: Timing scales from `short1` (50ms) to `extraLong4` (1000ms).\n* **`m3Springs`**: Hand-tuned spring constants (`fast`, `default`, `slow`) split into `spatial` (bouncy) and `effects` (critically damped).\n\n---\n\n## License\n\nApache-2.0\n","readmeFilename":"README.md"}