Provide a general summary of the issue here
When nesting <Tabs> inside another <Tabs> component, the transition animations severely break and can cause cause large layout shifts, when switching the outer <Tabs> component. This is caused by three distinct issues interacting with each other.
🤔 Expected Behavior?
- 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:
.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;
}
-
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.
-
At least mentioning somewhere that this is needed.
Workaround: Explicitly providing defaultSelectedKey to the nested <Tabs> so the inner <TabPanel> mounts synchronously
😯 Current Behavior
Issue 1: CSS Variable Inheritance (Bleed)
The outer <TabPanels> applies inline styles for --tab-panel-height and --tab-panel-width to handle transitions. However, CSS Custom Properties inherit downwards.
When a nested <TabPanels> 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 <TabPanels> applies the values of the outer <TabPanels> 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 <Tabs> component mounts while switching the Tab on the outer Tabs component, the inner <TabList> 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 <TabList>
Issue 3: TabPanel has 0 height oninitial render
When the nested <Tabs> component mounts, if defaultSelectedKey is not explicitly provided, the component takes one render frame to determine the active tab. During this frame, the nested <TabPanel> 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 <TabPanel>
💁 Possible Solution
No response
🔦 Context
Trying to setup a layout that needs to have nested Tabs.
🖥️ Steps to Reproduce
StackBlitz
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
Provide a general summary of the issue here
When nesting
<Tabs>inside another<Tabs>component, the transition animations severely break and can cause cause large layout shifts, when switching the outer<Tabs>component. This is caused by three distinct issues interacting with each other.🤔 Expected Behavior?
TabPanelsshould not inherit transition CSS variables from parentTabPanels. (Perhaps theTabPanelsshould set these variables initially, orTabPanelshould reset them toautofor nested childs).Workaround:
Outer
TabPanelsmeasuring logic should either wait for nested collections to finish building, orTabListshould hold its space synchronously orTabListshould hold on to a at least a minimum size.Workaround: Setting a hard
min-heighton theTabListCSS class to reserve the space during the initial render.At least mentioning somewhere that this is needed.
Workaround: Explicitly providing
defaultSelectedKeyto the nested<Tabs>so the inner<TabPanel>mounts synchronously😯 Current Behavior
Issue 1: CSS Variable Inheritance (Bleed)
The outer
<TabPanels>applies inline styles for--tab-panel-heightand--tab-panel-widthto handle transitions. However, CSS Custom Properties inherit downwards.When a nested
<TabPanels>mounts, it does not have set a inline style for--tab-panel-heightand--tab-panel-width. So while the transformation is ongoing the nested<TabPanels>applies the values of the outer<TabPanels>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
TabPanelsseems not always set the CSS custom properties and itIssue 2:
TabListhas 0 height on initial renderWhen the nested
<Tabs>component mounts while switching the Tab on the outerTabscomponent, the inner<TabList>initially renders with0pxheight.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
<TabList>Issue 3:
TabPanelhas 0 height oninitial renderWhen the nested
<Tabs>component mounts, ifdefaultSelectedKeyis not explicitly provided, the component takes one render frame to determine the active tab. During this frame, the nested<TabPanel>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<TabPanel>💁 Possible Solution
No response
🔦 Context
Trying to setup a layout that needs to have nested Tabs.
🖥️ Steps to Reproduce
StackBlitz
Switch between the Tabs on the upper
Tabselement (parent) in the Preview:Generalto the second TabAppearanceand notice normal behavior (This is the one I have modified withmin-heightonTabListand and `defaultSelectedKey``)Generaland notice the radical change on the layout within the transition.ProfileTab 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).AppearanceTabNotificationsand notice normal behavior (This is the one where I have added a class to the parentsTabPanelto 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