// FLIP: read the visible old pose before cancelling.
const dx = oldRect.left - newRect.left;
const dy = oldRect.top - newRect.top;
word.animate([
{ transform: `translate(${dx}px, ${dy}px)` },
{ transform: 'translate(0, 0)' }
], { duration: 420, easing: 'cubic-bezier(.22,1,.36,1)' });A little less
An answer with room to breathe. Pull upward to make it shorter.
How can I make a small room feel bigger?
Drag the little handle up or down. Or use the arrow keys.
Four prewritten versions. An interaction study, not a live AI service.
Key CSS & implementation tricks
.answer-wrap { position: relative; }
.answer { overflow: hidden; }
#handle { position: absolute; bottom: -22px; }
/* Animate measured height, never scale the text. */
card.animate([{ height: `${from}px` }, { height: `${to}px` }],
{ duration: 420, easing: 'cubic-bezier(.22,1,.36,1)' });// Read together, then write together.
const rects = words.map(word => word.getBoundingClientRect());
// Pointer events only schedule one update for the next frame.
if (!dragFrame) dragFrame = requestAnimationFrame(flushDrag);
// Words animate transform + opacity; no per-character blur.#handle { touch-action: none; cursor: ns-resize; }
handle.setPointerCapture(event.pointerId);
// Pointer cancellation and Escape restore the drag's start.
@media (prefers-reduced-motion: reduce) {
.answer { transition: none; }
}