Remove framer-motion from twenty-ui (#22021)

## What

Removes the `framer-motion` dependency from `twenty-ui` and replaces
every usage with pure CSS animations, reaching for Base UI primitives
where one fits:

- **Collapse/expand** (`AnimatedEaseInOut`,
`AnimatedExpandableContainer`): rebuilt on Base UI `Collapsible`
(CSS-animated `--collapsible-panel-height/width` + transition states).
Public props unchanged, so the ~28 call sites are untouched.
- **ProgressBar**: rebuilt on Base UI `Progress` (proper
`role`/`aria-valuenow`). The snackbar auto-dismiss countdown now uses a
CSS keyframe + `animation-play-state` (pause on hover), removing a
per-frame React re-render; `useProgressAnimation` is deleted.
- The remaining `Animated*` components, the circular spinner, checkmark,
and the placeholder pointer parallax move to plain CSS (SCSS modules +
the `duration()` helper + theme tokens).
- Deletes 3 unused components (`AnimatedTranslation`,
`AnimatedTextWord`, `AnimatedFadeOut`).

## Why

`twenty-ui` is a publicly published library with a size budget, so
dropping framer-motion shrinks what consumers ship. `twenty-front` keeps
its own framer-motion; that is out of scope here.

## Notes for reviewers

- A few `twenty-ui` components received framer props from `twenty-front`
call sites; those were migrated (e.g. `AnimatedLightIconButton` gained a
CSS `rotate` prop, and the `EMPTY_PLACEHOLDER_TRANSITION_PROPS` spreads
were removed).
- Behavior change: Base UI `Collapsible` animates only on open/close
transitions, so the old "animate in on first mount while already open"
case no longer plays (the `initial` prop is kept for API compatibility).

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/22021?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
This commit is contained in:
Raphaël Bosi
2026-06-23 17:43:08 +02:00
committed by GitHub
parent d4e4e2612b
commit 766d90af7e
67 changed files with 575 additions and 829 deletions
@@ -1,6 +1,6 @@
import { styled } from '@linaria/react';
import { useCallback, useContext } from 'react';
import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants';
import { useCallback } from 'react';
import { themeCssVariables } from 'twenty-ui/theme-constants';
import { RecordBoardColumnHeaderAggregateDropdown } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdown';
import { visibleRecordFieldsComponentSelector } from '@/object-record/record-field/states/visibleRecordFieldsComponentSelector';
@@ -114,8 +114,6 @@ const StyledRecordTableDragAndDropPlaceholderCell = styled.div`
`;
export const RecordTableRecordGroupSection = () => {
const { theme } = useContext(ThemeContext);
const currentRecordGroupId = useCurrentRecordGroupId();
const shouldHide = useShouldHideRecordGroup(currentRecordGroupId);
@@ -199,8 +197,7 @@ export const RecordTableRecordGroupSection = () => {
Icon={IconChevronDown}
size="small"
accent="secondary"
animate={{ rotate: !isRecordGroupTableSectionToggled ? -90 : 0 }}
transition={{ duration: theme.animation.duration.normal }}
rotate={!isRecordGroupTableSectionToggled ? -90 : 0}
/>
</StyledAnimatedLightIconButtonContainer>
</StyledChevronContainer>