Patterns from production · Streams that hold still

Smooth folds

Folds change height in place and remain interruptible.

Smooth foldsLive demo

Try the controls to see the rule.

The rule

Animate a fold between its measured open and closed heights. Cancel an older animation before starting another, and finish by returning layout to its natural height.

Tokens used

  • motion.duration.slow360ms
  • motion.easing.standardcubic-bezier(.2,.7,.2,1)
  • motion.easing.backcubic-bezier(.34,1.56,.64,1)
  • size.tap44px
  • space.md12px
  • motion.duration.base260ms

Native spec · generated from this pattern and the shared token registry

SwiftUI
struct OvSmoothFolds: View {
    let status: String
    @Environment(\.accessibilityReduceMotion) private var reduceMotion
    var body: some View {
        HStack(spacing: OvScale.spaceMd) {
            Text(status).font(.body)
        }
        .frame(minHeight: OvScale.sizeTap)
        .animation(reduceMotion ? .none : .easeOut(duration: OvScale.motionDurationBase), value: status)
    }
}
// Tokens: motion.duration.slow=360ms, motion.easing.standard=cubic-bezier(.2,.7,.2,1), motion.easing.back=cubic-bezier(.34,1.56,.64,1), size.tap=44px, space.md=12px, motion.duration.base=260ms
Compose
@Composable
fun OvSmoothFolds(status: String, reduceMotion: Boolean) {
    Row(
        modifier = Modifier.defaultMinSize(minHeight = OvScale.SizeTap),
        horizontalArrangement = Arrangement.spacedBy(OvScale.SpaceMd),
    ) {
        Text(status, style = MaterialTheme.typography.bodyMedium)
    }
    // Apply motion only when !reduceMotion.
}
// Tokens: motion.duration.slow=360ms, motion.easing.standard=cubic-bezier(.2,.7,.2,1), motion.easing.back=cubic-bezier(.34,1.56,.64,1), size.tap=44px, space.md=12px, motion.duration.base=260ms