Skip to content

## Bug: ease-sidebar-collapsed width transition causes layout thrashing and jank #1380

@Pcmhacker-piro

Description

@Pcmhacker-piro

Bug: ease-sidebar-collapsed width transition causes layout thrashing and jank

Description

The sidebar collapse/expand animation transitions the width property, which triggers layout recalculation on every animation frame. This causes visible jank, especially during expand (width increasing), as the browser must recalculate the layout of all sibling elements in real-time. On pages with complex layouts, this creates a stuttering animation.

Steps to Reproduce

  1. Link EaseMotion CSS v1.0.0
  2. Create a sidebar layout with content:
    <div class="ease-sidebar-layout">
      <aside class="ease-sidebar" id="sidebar">
        <nav>...</nav>
      </aside>
      <main class="ease-sidebar-main">
        <p>Lorem ipsum... (lots of content)</p>
      </main>
    </div>
  3. Toggle the sidebar collapse via ease-sidebar-collapsed class
  4. Record the animation in DevTools Performance tab

Expected Behavior

The sidebar collapse/expand animation should run at a smooth 60fps without causing layout shifts in the main content area during the animation.

Actual Behavior

The sidebar transitions width from 280px to 0 (or vice versa), which is a layout-triggering property. Each frame of the animation forces the browser to recalculate the layout of the main content area (.ease-sidebar-main) and all its children. On pages with complex DOM trees, this causes dropped frames and visible jank. The Performance tab shows a layout recalc on every animation frame rather than just compositing.

Implementation Hints

The issue is in components/sidebar.css:

/* Current implementation — animates width (layout-triggering) */
.ease-sidebar {
  width: var(--ease-sidebar-width, 280px);
  transition: width 0.3s ease;
  overflow: hidden;
  white-space: nowrap;
}

.ease-sidebar.ease-sidebar-collapsed {
  width: var(--ease-sidebar-collapsed-width, 60px);
}

The fix should use transform: translateX() combined with flex-basis instead of width, or use grid-template-columns with transition:

/* Option A — Use flex-basis + translate (GPU composited) */
.ease-sidebar-layout {
  display: flex;
}

.ease-sidebar {
  flex-shrink: 0;
  flex-basis: var(--ease-sidebar-width, 280px);
  overflow: hidden;
  transition: flex-basis 0.3s ease;
  /* The content inside uses translate to slide in/out */
}

.ease-sidebar.ease-sidebar-collapsed {
  flex-basis: var(--ease-sidebar-collapsed-width, 60px);
}

.ease-sidebar-main {
  flex: 1;
  min-width: 0;
  transition: flex-basis 0.3s ease;
}

/* Option B — Use CSS Grid with transition on grid-template-columns */
.ease-sidebar-layout {
  display: grid;
  grid-template-columns: var(--ease-sidebar-width, 280px) 1fr;
  transition: grid-template-columns 0.3s ease;
}

.ease-sidebar-layout.ease-sidebar-collapsed {
  grid-template-columns: var(--ease-sidebar-collapsed-width, 60px) 1fr;
}

/* Option C — Overlay approach (best performance) */
.ease-sidebar {
  position: fixed;
  top: var(--ease-navbar-height, 64px);
  left: 0;
  bottom: 0;
  width: var(--ease-sidebar-width, 280px);
  translate: 0 0;
  transition: translate 0.3s ease;
  z-index: 100;
}

.ease-sidebar.ease-sidebar-collapsed {
  translate: calc(-1 * (var(--ease-sidebar-width, 280px) - var(--ease-sidebar-collapsed-width, 60px))) 0;
}

.ease-sidebar-main {
  margin-left: var(--ease-sidebar-width, 280px);
  transition: margin-left 0.3s ease;
}

Option C (fixed positioning with translate) provides the best performance because translate is a composited property that doesn't trigger layout. The margin-left on the main content area is a layout property but only changes on the start/end of the transition rather than every frame when combined appropriately.

Affected Files

  • components/sidebar.css.ease-sidebar width transition, should use composited transform properties

Labels

type:bug, level:intermediate, GSSoC-26

Metadata

Metadata

Labels

No labels
No labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions