Skip to content

Accordion: animate the first time a section is opened - #3190

Open
jonahgoldsaito wants to merge 2 commits into
mainfrom
jg-accordion-first-open-animation
Open

Accordion: animate the first time a section is opened#3190
jonahgoldsaito wants to merge 2 commits into
mainfrom
jg-accordion-first-open-animation

Conversation

@jonahgoldsaito

@jonahgoldsaito jonahgoldsaito commented Aug 28, 2026

Copy link
Copy Markdown
Member
smooth-first-open.mp4

Summary

The AccordionSection component doesn't animate the first time it's opened was opened — it snapped open . Every toggle after that animates smoothly as intended

Root cause

Aphrodite merges each style list into a single generated class and injects that class's rule lazily. Swapping between wrapperCollapsed and wrapperExpanded pointed the first expand at a rule that hadn't been injected yet, so grid-template-rows computed to none — which cannot interpolate — and the section snapped open.

The caret icons rotation works because but none → rotate(180deg) is interpolable.

Fix

Now there's one stable class on the wrapper. Collapsing keys off the data-expanded attribute.

Both rules are injected together on first render, so no toggle ever meets a missing rule.

Precedent: attribute selectors in StyleSheet.create are established WB practice (action-styles.ts, cell-core.tsx, tab.tsx).

Verification

Claude took screengrabs and measured grid-template-rows frame by frame in the browser across the first open:

before after
first open 0 → 52px in one frame 0 → 0.65 → 3.9 → 11.3 → … → 52px over ~300ms

Confirmed the diagnosis before fixing: in a rounded-per-section accordion all sections share one merged class, so opening section 1 (snap) injected the rule that let section 2's first open animate. Same component, same props — which ruled out a layout-measurement cause.

Checked for all three cornerKinds, the collapse direction, animated={false} (still instant), mount-expanded (no transition on mount), and collapsible={false}. Collapsed content stays visibility: hidden; zero frames where the panel's overflow left hidden.

Jest (4/4 accordion suites, existing transition tests unchanged), typecheck, and lint all pass.

Reviewer notes

  • The frontend blast radius is zero. There are 40 render sites, but 0 snapshots render an accordion, 0 style-based assertions, 0 data-expanded collisions, 0 Cypress selectors. Every accordion test there asserts via aria-expanded. Yay!
  • The collapsed value now sits behind :not([data-expanded="true"]), which is more specific than the base class. A consumer overriding gridTemplateRows via style would still win when expanded but be ignored when collapsed.

AccordionSection with `animated` did not animate its expand the first
time a section was opened; it snapped. Every toggle after that animated
correctly.

Aphrodite merges each style list into a single generated class and
injects that class's rule lazily. Swapping between `wrapperCollapsed`
and `wrapperExpanded` therefore pointed the first expand at a rule that
had not been injected yet, so `grid-template-rows` computed to `none` --
which cannot interpolate -- and the section snapped open.

Keep one stable class on the wrapper instead, and select the collapsed
size off a `data-expanded` attribute. Both rules are injected together
on first render, so no toggle ever encounters a missing rule.

Verified in the browser: measured `grid-template-rows` frame by frame
across the first open for all three cornerKinds, which now ramps over
~300ms instead of jumping in a single frame.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Aug 28, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6047f84

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@khanacademy/wonder-blocks-accordion Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

npm Snapshot: Published

🎉 Good news!! We've packaged up the latest commit from this PR (2fb48c9) and published all packages with changesets to npm.

You can install the packages in frontend by running:

./dev/tools/deploy_wonder_blocks.js --tag="PR3190"

Packages can also be installed manually by running:

pnpm add @khanacademy/wonder-blocks-<package-name>@PR3190

@github-actions

Copy link
Copy Markdown
Contributor

Size Change: +18 B (+0.01%)

Total Size: 132 kB

📦 View Changed
Filename Size Change
packages/wonder-blocks-accordion/dist/es/index.js 3.04 kB +18 B (+0.6%)
ℹ️ View Unchanged
Filename Size
packages/eslint-plugin-wonder-blocks/dist/es/index.js 7.11 kB
packages/wonder-blocks-announcer/dist/es/index.js 2.43 kB
packages/wonder-blocks-badge/dist/es/index.js 2.03 kB
packages/wonder-blocks-banner/dist/es/index.js 2.01 kB
packages/wonder-blocks-birthday-picker/dist/es/index.js 1.93 kB
packages/wonder-blocks-breadcrumbs/dist/es/index.js 798 B
packages/wonder-blocks-button/dist/es/index.js 4.28 kB
packages/wonder-blocks-card/dist/es/index.js 1.09 kB
packages/wonder-blocks-cell/dist/es/index.js 2.19 kB
packages/wonder-blocks-clickable/dist/es/index.js 2.61 kB
packages/wonder-blocks-core/dist/es/index.js 2.6 kB
packages/wonder-blocks-data/dist/es/index.js 5.51 kB
packages/wonder-blocks-date-picker/dist/es/index.js 8.06 kB
packages/wonder-blocks-dropdown/dist/es/index.js 20.6 kB
packages/wonder-blocks-form/dist/es/index.js 6.39 kB
packages/wonder-blocks-grid/dist/es/index.js 1.25 kB
packages/wonder-blocks-icon-button/dist/es/index.js 4.06 kB
packages/wonder-blocks-icon/dist/es/index.js 1.89 kB
packages/wonder-blocks-labeled-field/dist/es/index.js 3.47 kB
packages/wonder-blocks-layout/dist/es/index.js 1.69 kB
packages/wonder-blocks-link/dist/es/index.js 1.54 kB
packages/wonder-blocks-modal/dist/es/index.js 7.36 kB
packages/wonder-blocks-pill/dist/es/index.js 1.32 kB
packages/wonder-blocks-popover/dist/es/index.js 4.41 kB
packages/wonder-blocks-progress-spinner/dist/es/index.js 1.49 kB
packages/wonder-blocks-search-field/dist/es/index.js 1.12 kB
packages/wonder-blocks-styles/dist/es/index.js 464 B
packages/wonder-blocks-switch/dist/es/index.js 1.6 kB
packages/wonder-blocks-tabs/dist/es/index.js 5.62 kB
packages/wonder-blocks-testing-core/dist/es/index.js 4.12 kB
packages/wonder-blocks-testing/dist/es/index.js 978 B
packages/wonder-blocks-theming/dist/es/index.js 384 B
packages/wonder-blocks-timing/dist/es/index.js 1.53 kB
packages/wonder-blocks-tokens/dist/es/index.js 6.5 kB
packages/wonder-blocks-toolbar/dist/es/index.js 906 B
packages/wonder-blocks-tooltip/dist/es/index.js 6.19 kB
packages/wonder-blocks-typography/dist/es/index.js 1.04 kB

compressed-size-action

@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

A new build was pushed to Chromatic! 🚀

https://5e1bf4b385e3fb0020b7073c-bhhyxxeorw.chromatic.com/

Chromatic results:

Metric Total
Captured snapshots 21
Tests with visual changes 0
Total stories 867
Inherited (not captured) snapshots [TurboSnap] 520
Tests on the build 541

The changeset blamed a changed class name, but Wonder Blocks' Jest
inlines Aphrodite styles, so the wrapper renders class="" and no class
name appears in snapshots at all. The consequence worth warning about
is the one on the inlining path: nested selectors are not applied
there, so a collapsed wrapper reports grid-template-rows:
min-content 1fr and reads as expanded.

Also record, next to the selector, that the collapsed value outranks a
consumer `style` override of the same property.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@jonahgoldsaito
jonahgoldsaito marked this pull request as ready for review August 28, 2026 20:40
@khan-actions-bot
khan-actions-bot requested a review from a team August 28, 2026 20:40
@codecov

codecov Bot commented Aug 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 0.00%. Comparing base (aeaeddd) to head (6047f84).

Additional details and impacted files

Impacted file tree graph

@@     Coverage Diff      @@
##   main   #3190   +/-   ##
============================
============================

Continue to review full report in Codecov by Harness.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update aeaeddd...6047f84. Read the comment docs.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@jandrade jandrade left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! and it is in line with the ongoing CSS modules migration. Thanks for your contribution! hoping to get more of this 👏 🚢

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants