Skip to content

feat(booker): animate step 1→2 with motion instead of custom SizeTransition - #814

Draft
pasqualevitiello wants to merge 51 commits into
feat/atomsfrom
devin/1783358765-booker-motion
Draft

feat(booker): animate step 1→2 with motion instead of custom SizeTransition#814
pasqualevitiello wants to merge 51 commits into
feat/atomsfrom
devin/1783358765-booker-motion

Conversation

@pasqualevitiello

@pasqualevitiello pasqualevitiello commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Summary

Replaces the hand-rolled SizeTransition primitive with motion for the Booker step 1 (select) → step 2 (confirm) card resize. Reverts the two SizeTransition commits (527060a9, f22fbde7) back to the a72a4b26 state, then animates the step container directly.

Motivation: the custom impl cross-faded a cloneNode(true) snapshot of the outgoing step, a visual-only copy that dropped runtime state — most visibly, scrolling the time list and picking a slot snapped the clone back to scrollTop: 0, causing a jump on transition start (plus focus/media/portals and duplicate DOM ids). Hardening it would have re-implemented a worse AnimatePresence. motion animates the real, mounted nodes, so that whole class of bugs goes away.

Approach

motion's layout prop fakes size changes with transform: scale — it snaps the box to its final size and scales, so the surrounding layout doesn't actually resize and you get a visible scale/distortion. Instead this drives the real width/height with a FLIP measure + imperative animate(), while AnimatePresence cross-fades the two mounted panels:

// container is a plain div we size imperatively; only the active step is mounted
<LazyMotion features={domMax}>
  <MotionConfig reducedMotion="user" transition={{ duration: 0.45, ease: [0.32,0.72,0,1] }}>
    <div ref={stepContainerRef} className="relative w-full @3xl:flex-1 overflow-clip">
      <AnimatePresence initial={false} mode="popLayout">
        {step === "select"
          ? <m.div key="select"  ref={selectStepRef}  initial/animate/exit={opacity}></m.div>
          : <m.div key="confirm" ref={confirmStepRef} initial/animate/exit={opacity}></m.div>}
      </AnimatePresence>
    </div>
  </MotionConfig>
</LazyMotion>

// useIsomorphicLayoutEffect on [step]:
const start = { width, height } = stepContainer;          // current (old) box
stepContainer.style.{width,height} = "auto";              // release
enteringStep.style.removeProperty("width");               // unpin
const end = { width, height } = stepContainer;            // natural (target) box
enteringStep.style.width = `${end.width}px`;              // pin entering panel → no reflow, clipped
stepContainer.style.{width,height} = `${start.*}px`;      // set start
animate(stepContainer, { width: end.width, height: end.height }, { duration, ease });
// on finish (guarded by a token for rapid interruption): clear inline styles

Behavior mapping to the request:

  • fade out step 1 / fade in step 2opacity initial/animate/exit on the keyed panels; mode="popLayout" overlaps them (simultaneous cross-fade) and freezes the exiting panel's box.
  • animate container width + height → real width/height via animate() (no transform, no scale, no slide); the card box genuinely resizes so the meta side follows.
  • preserve dimensions + clip → entering panel pinned to its natural width during the resize + overflow-clip on the container.
  • reduced motionreducedMotion="user" for the opacity, and the size animate() uses duration: 0 when prefers-reduced-motion: reduce.

Kept the design-system easing/duration (duration 0.45, cubic-bezier(0.32,0.72,0,1)). LazyMotion + domMax keeps the feature bundle code-split (domMax required for AnimatePresence popLayout). Pinned motion@12.42.0 (exact).

Advice on the "animate the Card instead of the steps container" option: keeping it on the steps container is better — the Card's meta column is fixed-width, so animating the container avoids the meta side scaling/reflowing; only the step region should move.

Registry: added "motion" to the booker-1 atom dependencies and regenerated public/r.

Notes:

  • Draft per repo convention.
  • The pre-existing registry:validate-deps failure for table is unrelated (present on the base branch) and left untouched.

Link to Devin session: https://app.devin.ai/sessions/0e0c577537ce42fdac18e9eec4224bbc
Requested by: @pasqualevitiello

@pasqualevitiello pasqualevitiello self-assigned this Jul 6, 2026
@devin-ai-integration

Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@vercel

vercel Bot commented Jul 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
coss-com Ready Ready Preview Sep 8, 2026 2:56pm UTC
coss-com-origin Ready Ready Preview Sep 8, 2026 2:56pm UTC
coss-com-ui Ready Ready Preview Sep 8, 2026 2:56pm UTC
coss-examples-calcom Ready Ready Preview Sep 8, 2026 2:56pm UTC

Request Review

Co-Authored-By: pasquale <pasqualevitiello@gmail.com>
devin-ai-integration Bot and others added 2 commits July 6, 2026 17:54
Co-Authored-By: pasquale <pasqualevitiello@gmail.com>
…esence crossfade

Co-Authored-By: pasquale <pasqualevitiello@gmail.com>
Co-Authored-By: pasquale <pasqualevitiello@gmail.com>
pasqualevitiello and others added 22 commits July 13, 2026 17:02
* initial commit

* fix coss logo

* update card + build reg

* mc

* mc

* format

* mc
* initial attempt

* mc

* autohighlight

* feat: add team members selectors

* mc
* feat: upgrade React DayPicker to v10

Migrate coss packages, examples, registry output, and documentation to the preferred scoped package while keeping the origin app on v9.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: emove redundant transition

* docs: generalize DayPicker version reference

Co-authored-by: Cursor <cursoragent@cursor.com>

* update docs

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
`--spacing(0)` compiles to unitless `0`; popup transforms add it
inside calc() (`translateY(calc(100% + … + var(--inset)))`). Adding
a number to a length is invalid calc → transform dropped → popup
never starts off-screen, so it fades instead of sliding. All
positions affected. `0px` keeps the calc a valid length.

Closes #803

Co-authored-by: Vasilii Boldurean <vasilii.boldurean@computacenter.com>
* fix(input-group): prevent addon text from being cut off

Remove leading-none so line-clamp/truncate no longer clips InputGroupText.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(input-group): use truncate instead of line-clamp on addon text

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
* feat(scroll-area): make overscroll contain opt-in

Stop applying overscroll-contain by default and expose overscrollContain
so nested surfaces can opt in without forcing it on every ScrollArea.

Co-authored-by: Cursor <cursoragent@cursor.com>

* reg build

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(input): set autofill text color on the input element

Ensure autofilled text uses foreground color in embedded iframes where
browser autofill styles otherwise override the wrapper-level color.

Co-authored-by: Cursor <cursoragent@cursor.com>

* chore(registry): sync input.json after autofill fix

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(input): move text-foreground onto the input element

Keep autofill text color and foreground text on the input itself for
consistent styling in embedded iframes.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(ui): move text color onto form control elements

Apply text-foreground (and related styles) on textarea, number-field,
and combobox inputs for consistent autofill behavior in iframes.

Co-authored-by: Cursor <cursoragent@cursor.com>

* chore(registry): sync combobox and number-field JSON

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
* feat(ui): add segmented control patterns

* docs(ui): add tabs migration guidance

* docs(ui): mark segmented control as new

* fix(ui): add segmented control particle tag

* fix(ui): clarify segmented particle semantics

* fix(ui): exclude tabs from segmented search

* style(ui): remove segmented state transition

* fix(ui): require segmented radio selection

* fix(ui): remove redundant radio requirement

* fix(ui): keep segmented toggle selected

* style(ui): preserve segmented text transition

* revert(ui): allow empty segmented toggles

* style(ui): remove segmented transitions

* style(ui): isolate segmented focus transition

* fix(ui): refine segmented control spacing

* docs(skills): add segmented control guidance

* refactor(ui): remove segmented toggle particles
Extract icon gap, size, and opacity into segmentedControlItemLayoutClassName so radio, navigation, and Tabs stay optically aligned.

Co-authored-by: Cursor <cursoragent@cursor.com>
)

Co-authored-by: Cursor <cursoragent@cursor.com>
…nimates (#834)

Tailwind's utility is singular, so `transition-shadows` compiles to nothing: the viewport gets no transition-property or duration and its focus-visible ring snaps in and out. `transition-shadow` sets `transition-property: box-shadow`, which is what `ring-2` and `ring-offset-1` render.

The registry JSON and the packages/ui copy carry the same change.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(toast): keep anchored toasts above later-opened popovers

The anchored toast portal stays mounted at z-50, so a popover opened afterward stacks on top of it.

Co-authored-by: Cursor <cursoragent@cursor.com>

* change z-index

* build reg

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
* feat: migrate TanStack Table to v9

* perf: optimize TanStack Table state updates

* fix: restore team members table selection updates

Subscribe only re-renders when its child is a function, so static JSX left checkboxes stale until a sort. Match the members table: subscribe with a render function and toggle rows on click.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
…er-motion

Co-authored-by: Cursor <cursoragent@cursor.com>

# Conflicts:
#	bun.lock
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants