perf(motion): lazy-load framer-motion via LazyMotion + m (~12KB gzip/component) - #334
Open
DipeshRajoria007 wants to merge 1 commit into
Open
perf(motion): lazy-load framer-motion via LazyMotion + m (~12KB gzip/component)#334DipeshRajoria007 wants to merge 1 commit into
m (~12KB gzip/component)#334DipeshRajoria007 wants to merge 1 commit into
Conversation
Wrap the animated components (Modal, Drawer, BottomSheet, Overlay, PopOver, Accordion, DropdownMenu) in a shared <GrauityLazyMotion> provider and swap eager `motion.*` for the lightweight `m.*` component with the `domAnimation` feature bundle. `domAnimation` covers initial/animate/exit/variants/transition (every animation these components use) while excluding the drag/layout/pan/ gesture features that eager `motion.*` always bundles. No public API or runtime-behaviour change. Per animated component this trims ~12 KB gzip (~41 KB raw, ~14% of the component's bundle) of unused framer-motion feature code. - common/motion.tsx: shared LazyMotion(domAnimation) provider - *.styles.ts: styled(motion.div) -> styled(m.div) - *.tsx: wrap component return in <GrauityLazyMotion>; Accordion inline motion.div -> m.div - __mocks__/framer-motion.tsx: add m / LazyMotion / domAnimation / domMax Verified: tsc, eslint, prettier, jest (46 suites / 412 tests), tsup build, and a real-browser Playwright run (modal enter, AnimatePresence exit under LazyMotion, accordion expand) with zero framer-motion / LazyMotion console errors. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
MihirSachdeva
added a commit
that referenced
this pull request
Jun 16, 2026
…SSR fix (#336) ## Summary Consolidated **performance** release of grauity, cut as **`3.4.0-beta.2`** and validated against real consumer apps. This is the integration branch combining the four perf PRs **plus** the fixes found during beta testing, merged up to date with `master`. ## What's in it **Perf suite (PRs #332–#335):** - **#332** — static token CSS + Tailwind `@theme` export; drops runtime `createGlobalStyle`/`extractColorVariables` injection (`./tokens.css`, `./theme.css`). - **#333** — tree-shakeable per-module **tsup** build (replaces Parcel) + `exports` map + precise `sideEffects` + code-splitting. - **#334** — framer-motion **`LazyMotion`** + `m` + `domAnimation` (~12 KB gz/animated component). - **#335** — **lodash → lodash-es** (tree-shaken) + inline Icon utils (~68 KB raw / ~13 KB gz off dist). **Fixes found during beta testing (the reason for beta.1/beta.2):** - **SSR crash fix** — switched all 46 source files from `import styled` (default) to the **named** `import { styled }`. styled-components v6's *default* export is `undefined` under Node's native ESM loader (which Next.js uses during "Collecting page data"), so the previous beta crashed consumer builds (`styled.i.attrs` of undefined). The named import works under Node-ESM, CJS, and bundlers — no `node`-condition workaround needed. - **Barrel tree-shaking fix** — tsup entry glob `index.ts` → `index.{ts,tsx}`, so the 9 `.tsx` components (PopOver, SelectDropdown, Tooltip, DropdownMenu, RangeInput, FloatingActionButton, Form/{Dropdown,FormRow,TextArea}) get their own chunks instead of being inlined into the barrel. Result: `import { NSButton } from '@newtonschool/grauity'` drops framer-motion + yup — **71.5 KB → 14.3 KB gz** — so barrel consumers get the tree-shaking win with **no import changes**. **Merged up to `master`:** includes the transient-props styled-components refactor (#327), `3.3.13`, and the new icons (#330, #331). The `Modal.{tsx,styles.ts}` conflicts were resolved by keeping master's transient-props refactor **and** the LazyMotion/named-import changes. **Build fix:** added the `@rollup/rollup-linux-x64-gnu` install entry to `package-lock.json` (the lock had been regenerated on macOS, dropping the Linux rollup binary → the publish CI's `npm ci` failed on `tsup`; npm/cli#4828). ## Verification (no regressions) - `tsc --noEmit`, `eslint`, `prettier --check`, **`jest` 46 suites / 412 tests**, `tsup` build — all green on the merged tree. - Node native-ESM `import()` of the barrel + subpaths resolves (SSR crash gone); browser tree-shaking preserved. - **Real consumers on `3.4.0-beta.1`:** newton-web (`next build` — 258/258 pages, the page that crashed on beta.0 now builds) and newton-school-public-website — **both CI green**. ## Publishing notes - Publish under the **`beta`** dist-tag — the workflow's `npm-publish` defaults to `latest`, which would clobber `3.3.13` for all consumers. After publishing, run `npm dist-tag add @newtonschool/grauity@3.3.13 latest` to restore stable. - Source PRs #332–#335 remain open; this PR is the consolidated beta. The team decides whether to merge this combined PR or land them individually for the stable `3.4.0`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Mihir Sachdeva <mihirsachdeva.ms@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Lazy-load framer-motion features in grauity's animated components via
LazyMotion+ the lightweightmcomponent, instead of importing the full eagermotion.*API.A new shared provider
common/motion.tsx:The 7 animated components — Modal, Drawer, BottomSheet, Overlay, PopOver, Accordion, DropdownMenu — now:
<GrauityLazyMotion>,styled(m.div)instead ofstyled(motion.div)(*.styles.ts),m.*instead of inlinemotion.*(Accordion).Why (grauity perf)
domAnimationbundles initial / animate / exit / variants / transition — every animation these components actually use — while excluding thedrag/layout/pan/ gesture features that eagermotion.*always pulls in. grauity uses none of those excluded features.Measured impact
Bundling each animated component on its own (esbuild, minified,
react/react-domexternal), before (motion) vs after (m+LazyMotion):~12 KB gzip (~41 KB raw, ~14%) off every animated component, no public API or runtime-behaviour change.
No public API change
Component props, exports, and animation behaviour are unchanged. This builds on the tree-shakeable tsup build (base branch
build/tsup-tree-shaking), so consumers that don't import an animated component pay nothing, and those that do now ship less framer-motion.Testing — no regression
tsc --noEmit: ✅ cleaneslint: ✅ cleanprettier --check: ✅ cleanjest: ✅ 46 suites / 412 tests passtsup build-lib: ✅ builds, types + dts shim emittedLazyMotion):m.div+LazyMotion) — ✅ PASS#modal-bodypresent mid-exit, then unmounted — ✅ PASS (this is the critical check — confirmsAnimatePresenceexit animation works underLazyMotion/domAnimation)m.div) — ✅ PASSFiles
common/motion.tsx— new sharedLazyMotion(domAnimation)providerui/elements/{Modal,Drawer,BottomSheet,Overlay,PopOver,Accordion,DropdownMenu}/*— wrap return in<GrauityLazyMotion>;motion.*→m.*__mocks__/framer-motion.tsx— addm/LazyMotion/domAnimation/domMaxto the test mock🤖 Generated with Claude Code