Nested `Tabs` height transitions break due to CSS variable bleed and 0-height behavior of `TabList` and `TabPanel`
- Dominant language
- TypeScript
- Stars
- 15.9k
- Forks
- 1.6k
- Avg merge
- 3d 9m
- Merged PRs (30d)
- 59
Description
### Provide a general summary of the issue here
When nesting `` inside another `` component, the transition animations severely break and can cause cause large layout shifts, when switching the outer `` component. This is caused by three distinct issues interacting with each other.
### 🤔 Expected Behavior?
1. Nested `TabPanels` should not inherit transition CSS variables from parent `TabPanels`. (Perhaps the `TabPanels` should set these variables initially, or `TabPanel` should reset them to `auto` for nested childs).
**Workaround:**
```css
.react-aria-TabPanels {
position: relative;
height: var(--tab-panel-height);
width: var(--tab-panel-width);
/* .... */
}
.react-aria-TabPanel {
padding: var(--spacing-3);
border-radius: var(--radius);
/* .... */
--tab-panel-width: auto; /* <-- Reset the variable for nested TabPanels */
--tab-panel-height: auto;
}
```
2. Outer `TabPanels` measuring logic should either wait for nested collections to finish building, or `TabList` should hold its space synchronously or `TabList` should hold on to a at least a minimum size.
**Workaround:** Setting a hard `min-height` on the `TabList` CSS class to reserve the space during the initial render.
3. At least mentioning somewhere that this is needed.
**Workaround:** Explicitly providing `defaultSelectedKey` to the nested `` so the inner `` mounts synchronously
### 😯 Current Behavior
#### Issue 1: CSS Variable Inheritance (Bleed)
The outer `` applies inline styles for `--tab-panel-height` and `--tab-panel-width` to handle transitions. However, CSS Custom Properties inherit downwards.
When a nested `` mounts, it does not have set a inline style for `--tab-panel-height` and `--tab-panel-width`. So while the transformation is ongoing the nested `` applies the values of the outer `` which are bigger than they should.
It ends up temporarily inheriting the parent's explicit pixel height, throwing off the layout.
This issue happens not always as the `TabPanels` seems not always set the CSS custom properties and it
#### Issue 2: `TabList` has 0 height on initial render
When the nested `` component mounts while switching the Tab on the outer `Tabs` component, the inner `` initially renders with `0px` height.
This causes the transition logic to calculate the wrong height for the transition. Which causes a layout shift right after the transition, which is exactly the height of the ``
#### Issue 3: `TabPanel` has 0 height oninitial render
When the nested `` component mounts, if `defaultSelectedKey` is not explicitly provided, the component takes one render frame to determine the active tab. During this frame, the nested `` renders with 0px height. This causes the transition logic to calculate the wrong height for the transition. Which causes a **large** layout shift right after the transition, which is exactly the height of that ``
### 💁 Possible Solution
_No response_
### 🔦 Context
Trying to setup a layout that needs to have nested Tabs.
### 🖥️ Steps to Reproduce
[StackBlitz](https://stackblitz.com/edit/8ypukkum?file=src%2FExample.tsx)
Switch between the Tabs on the upper `Tabs` element (parent) in the Preview:
- Switch form the first `General` to the second Tab `Appearance` and notice normal behavior (This is the one I have modified with `min-height` on `TabList` and and `defaultSelectedKey``)
- Switch back to the first Tab `General` and notice the radical change on the layout within the transition.
- Switch to the third `Profile` Tab and notice the text and the input fields on the nested Tab shifting in size because the container is changing the size (Noticeable shift on the text might be depending on preview window size - try changing it if you cant notice it).
- Switch back to the second `Appearance` Tab
- Switch to the last Tab `Notifications` and notice normal behavior (This is the one where I have added a class to the parents `TabPanel` to stop inheriting the wrong css variable)
### Version
react-aria: 3.50.0 - react-aria-components: 1.19.0
### What browsers are you seeing the problem on?
Chrome
### If other, please specify.
_No response_
### What operating system are you using?
Windows
### 🧢 Your Company/Team
_No response_
### 🕷 Tracking Issue
_No response_
Contributor guide
Research direction
Start with the StackBlitz reproduction in src/Example.tsx using the reported react-aria 3.50.0 and react-aria-components 1.19.0 versions. Reproduce nested Tabs while switching the outer tabs, then verify that nested panels no longer inherit incorrect CSS variables and that TabList and TabPanel height transitions do not cause layout shifts or require the documented workarounds.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- react, typescript
- Domain
- frontend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100