ABP 10.5 Angular Lepton Style 4 sidebar submenu issue
Summary
After upgrading an Angular application to ABP 10.5, sidebar parent menu items can become current and update the expanded-route state while their nested submenu remains hidden. The SaaS parent is a reproducible example: the host user can retrieve the tenant data, but the Tenants and Editions submenu entries do not become visible.
The application uses Lepton Style 4 and the corresponding lepton4 CSS bundle. The click and authorization flow are working; the failure occurs in the final CSS/layout state.
Environment
- ABP Framework packages:
10.5.0 - Angular:
21.2.x - Theme package:
@volo/abp.ng.theme.leptonversion10.5.0 - Lepton theme style: Style 4 (
lepton4bundle) - Application layout: left/opened sidebar (
lp-opened-sidebar) - User context: authenticated host user
ABP Studio solution configuration
ABP Studio's Solution Configuration reports:
- Template:
app - Template type:
Classic - Created ABP Studio version:
0.8.1 - Current ABP Studio version:
3.0.6 - Tiered:
Yes - UI framework:
Angular - Theme:
LeptonX(not sure why - we're using Lepton classic) - Theme style:
system - Progressive Web App:
No - Database provider:
EF - Database management system:
SQL Server - Separate tenant schema:
Yes - Mobile framework:
none - Public website:
No - Optional modules: GDPR, TextTemplateManagement, LanguageManagement, AuditLogging, SaaS, and OpenIddictAdmin
There is an apparent metadata/runtime discrepancy: Solution Configuration identifies the theme as LeptonX, while the current runtime source uses the legacy Lepton Angular package (@volo/abp.ng.theme.lepton), provideThemeLepton(), the legacy Volo.Abp.LeptonTheme server modules, and Lepton Style 4 (lepton4.css). This should be considered when diagnosing the issue.
Reproduction
- Sign in as a host user.
- Open the SaaS sidebar menu.
- Observe that the parent menu receives the
currentclass. - Observe that the nested items remain invisible.
The route state contains the expected expanded SaaS entry:
expandedRoutes = Set(1) { 'Saas::Menu:Saas1' }
The route node contains the expected child entries (Tenants and Editions), and the API returns the expected tenant data.
Browser evidence
- The parent click handler executes.
expandedRoutesis updated.- The parent route receives
current. - The submenu contains its child route elements.
- The submenu's computed
displayremainsnone. - The document body has
abp-application-layout lp-opened-sidebar lp-body-fixed. - The rendered submenu does not contain the
d-block overflow-hiddenclass that the LeptonRoutesComponentuses for the sidebar placement.
The loaded Lepton Style 4 CSS contains the rule that hides nested sidebar lists:
.lp-opened-sidebar .lp-sidebar .lp-sidebar-navi > ul ul {
display: none;
}
This indicates a layout/CSS state mismatch: the route is marked current and expanded, but the template does not apply the class that makes the nested list visible for the active layout.
The console warning observed during diagnosis concerned Angular animation of overflow. It is not the cause of the hidden submenu.
Application-side configuration audit
The application-side configuration was checked for interference:
- Lepton menu placement is configured as
Left. - Lepton menu status is configured as
AlwaysOpened. - Lepton Style 4 is selected, and the
lepton4bundle is loaded. - The standard Lepton provider is used.
- No custom replacement is registered for
RoutesComponent,abp-routes, or the sidebar component. - No application code sets the menu placement to
Topor manually manages the Lepton body layout classes. - Custom sidebar CSS does not hide nested menus. The only visibility override is the workaround below.
The body correctly reports the left-sidebar state, so the mismatch appears to be within Lepton's layout state/route component interaction rather than an application override.
Current workaround
The application keeps current submenus visible in the opened sidebar:
// ABP 10.5/Lepton can render a sidebar route as `current` while its layout
// state omits the template's `d-block` class. lepton4.css then hides every
// nested UL under `.lp-opened-sidebar`, leaving expanded submenus at display:none.
// Keep current sidebar submenus visible until the upstream layout/CSS mismatch
// is resolved.
.lp-opened-sidebar .lp-sidebar .lp-sidebar-navi ul li.current > ul {
display: block !important;
}
After applying this workaround, the SaaS submenu opens correctly.
Lepton package verification
The application uses the latest stable Lepton package currently available from the npm registry:
@volo/abp.ng.theme.lepton:10.5.0- Lepton Style 4 is sourced from the package's
lepton4.min.css.
The 10.6.0-rc.1 package was also inspected. Its lepton4.min.css is unchanged, and its compiled RoutesComponent retains the same isMenuPlacementTop() && !smallScreen() conditions. It does not appear to fix this issue. It is built for the ABP 10.6/Angular 22 package line and should be evaluated only as part of a coordinated upgrade.
Request to ABP
Please confirm whether this is a known regression or layout detection issue in the Angular Lepton Style 4 theme. Specifically:
- Why can
RoutesComponentrender a sidebar route withoutd-block overflow-hiddenwhile the document is inlp-opened-sidebarmode? - Should the component use the actual sidebar/body layout state instead of the menu-placement state when deciding submenu visibility?
- Is an upstream fix planned for a subsequent 10.5 patch or the ABP 10.6 release?
The following references may be relevant, although neither is an exact match to this reproduction:
- ABP Support question 10671 — related Lepton parent/submenu behavior, but Blazor-specific.
- ABP GitHub issue 25709 — related Angular LeptonX navigation behavior.
1 Answer(s)
-
0
Hi,
Confirmed, this is a real bug in the classic Lepton Angular theme. The missing classes are the cause — the layout condition itself evaluates correctly, so it isn't a layout-detection issue, and it isn't specific to Style 4.
The nested submenu
<ul>in the sidebar routes component applies its visibility class through an Angular object binding, roughly:<ul class="dropdown-ul" [class]="{ 'd-block overflow-hidden': ... }">That key is a single string holding two space-separated classes (
d-block overflow-hidden). Angular's native[class]binding doesn't split a multi-word key — it drops any object key that contains a space before it reaches the DOM.[ngClass]splits on whitespace and applies both classes, so when the binding was switched from[ngClass]to[class],d-blockstopped landing on the<ul>. Withoutd-block, the theme's nested-menu rule (.lp-opened-sidebar ... > ul ul { display: none; }) keeps the<ul>atdisplay: none, even though the route iscurrentandexpandedRoutesis updated.To your three questions:
- The route renders without
d-block overflow-hiddenbecause the class binding silently discards the multi-word key, not because of the sidebar/body layout state. For the opened left sidebar, the placement/small-screen condition resolves to "apply the class". - No layout-state change is needed — the condition logic is fine. The fix is on the binding: splitting the key into
{ 'd-block': ..., 'overflow-hidden': ... }(or going back to[ngClass]) makes both classes apply again. - We've confirmed it as a bug and will prepare the fix. We'll update this ticket once the target release is set.
One note on scope: this isn't new in 10.5. The binding changed in 10.2.0 and is still present in 10.6.0-rc.1, and it affects every Lepton style (1–6), since they all share the same nested-
ulhide rule — Style 4 is just the one you're on. That's also why 10.6.0-rc.1 doesn't change anything here.Your CSS workaround is a valid interim fix, so you can keep it until the patch ships.
Thanks
Markdown supported.Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post) - The route renders without