Forum Discussion
Gavin-Williams
Feb 26, 2023Brass Contributor
Convert the standard Blazor navigation menu to a collapsible icon menu
While I admittedly love Blazor I’ve always changed the out-of-the-box navigation menu that comes with it. It’s the first manoeuvre I pull when spinning up a new Blazor app, stripping out the purpl...
jassen777
Feb 03, 2024Copper Contributor
If it helps anyone else...With Blazor Web App on NET8, you need to add the open-iconic css and add the rendermode. After making these additions, the code supplied by Gavin-Williams worked great. Thank you Gavin-Williams for putting together.
In app.css, at the top of the page add:
@import url('https://cdnjs.cloudflare.com/ajax/libs/open-iconic/1.1.1/font/css/open-iconic-bootstrap.min.css');
In app.razor make sure to add the RenderMode in both the HeadOutlet and Routes (why not?):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="/" />
<link rel="stylesheet" href="bootstrap/bootstrap.min.css" />
<link rel="stylesheet" href="app.css" />
<link rel="stylesheet" href="BlazorApp1.styles.css" />
<link rel="stylesheet" href=""
<link rel="icon" type="image/png" href="favicon.png" />
<HeadOutlet @rendermode="RenderMode.InteractiveServer" />
</head>
<body>
<Routes @rendermode="RenderMode.InteractiveServer" />
<script src="_framework/blazor.web.js"></script>
</body>
</html>
- MMaybe990Apr 03, 2024Copper Contributor
jassen777 Thank you so much for this! I was tearing my hair out trying to figure out why none of my ideas for collapsing menus worked. I also added the option to have sub menu items with those icons available in the Menu Icon section. Here in my code.
@rendermode InteractiveServer <div class="navbar-wrapper"> <div class="top-row ps-3 navbar navbar-dark"> <div class="container-fluid"> <a href="/dashboard"><img src="images/NewLogo-transparent.png" alt="Brand Logo" width="30" height="30"></a> @if (!@IconMenuActive) { <a class="navbar-brand">Brand option</a> } <button title="Navigation menu" class="navbar-toggler" @onclick="ToggleNavMenu"> <span class="navbar-toggler-icon"></span> </button> </div> </div> <div class="@NavMenuCssClass" @onclick="ToggleNavMenu"> <nav class="flex-column"> <div class="nav-item px-3" @onclick="ToggleSubMenu"> <NavLink class="nav-link flex-container" Match="NavLinkMatch.All"> <span class="oi oi-dollar" aria-hidden="true" title="Home"></span> @if (!@IconMenuActive) { <label>Accounting</label> <span class="oi chevron-icon @(showSubMenu ? "oi-chevron-top" : "oi-chevron-bottom") ps-3" style="font-size: 1rem;" aria-hidden="true"></span> } </NavLink> <div class="sub-menu" style="display: @(showSubMenu ? "block" : "none")"> <NavLink class="nav-link" href="subitem1"> <span class="oi oi-folder" aria-hidden="true" title="Sub Item 1"></span> @if (!@IconMenuActive) { <label>Home</label> } </NavLink> <NavLink class="nav-link" href="subitem2"> <span class="oi oi-document" aria-hidden="true" title="Sub Item 2"></span> @if (!@IconMenuActive) { <label>Sub Menu Item 1</label> } </NavLink> </div> </div> <div class="nav-item px-3"> <NavLink class="nav-link" href="counter"> <span class="oi oi-calculator" aria-hidden="true"></span> @if (!@IconMenuActive) { <label>Sub Menu Item 2</label> } </NavLink> </div> <div class="nav-item px-3"> <NavLink class="nav-link" href="fetchdata"> <span class="oi oi-list-rich" aria-hidden="true"></span> @if (!@IconMenuActive) { <label>Fetch data</label> } </NavLink> </div> </nav> </div> <div class="bottom-row"> <div class="icon-menu-arrow"> @if (!@IconMenuActive) { <span class="oi oi-arrow-left" style="color: black;" @onclick="ToggleIconMenu"></span> } else { <span class="oi oi-arrow-right" style="color: black;" @onclick="ToggleIconMenu"></span> } </div> </div> </div> @code { private bool showSubMenu = false; private void ToggleSubMenu() { showSubMenu = !showSubMenu; } //bool to send to MainLayout for shrinking sidebar and showing/hide menu text private bool IconMenuActive { get; set; } = false; //EventCallback for sending bool to MainLayout [Parameter] public EventCallback<bool> ShowIconMenu { get; set; } private bool collapseNavMenu = true; private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null; private void ToggleNavMenu() { collapseNavMenu = !collapseNavMenu; } //Method to toggle IconMenuActive bool and send bool via EventCallback private async Task ToggleIconMenu() { IconMenuActive = !IconMenuActive; await ShowIconMenu.InvokeAsync(IconMenuActive); } }
I'm still working on the styling but here's that as well.
.navbar-wrapper { border-right: 1px solid #545454; min-height: 100vh; overflow: hidden; } .a:hover,a:active{ color: black; } .navbar-toggler { background-color: rgba(255, 255, 255, 0.1); margin-bottom: 20px; } .top-row { height: 3.5rem; background-color: white; border-bottom: 1px solid #d6d5d5; } .top-row ::deep a.active, .top-row ::deep a:hover { color: black; background-color: transparent; } .bottom-row { position: absolute; bottom: 3rem; padding-bottom: 10px; text-align: right; width: 100%; padding-right: 28px; } .icon-menu-arrow { text-align: right; } .navbar-brand { font-size: 1.1rem; color: black; } .oi { width: 2rem; font-size: 1.1rem; vertical-align: text-top; top: -2px; transition: ease-in-out 0.3s; transform-origin: center; } .chevron-icon{ transition: transform 0.3s ease-in-out; } oi-chevron-top{ transform: rotate(180deg); } .nav-item { font-size: 0.9rem; padding-bottom: 0.5rem; } .nav-item:first-of-type { padding-top: 1rem; } .nav-item:last-of-type { padding-bottom: 1rem; } .nav-item ::deep a { color: #000000; border-radius: 4px; height: 3rem; display: flex; align-items: center; line-height: 3rem; } .nav-item ::deep a.active { background-color: #545454; color: white; } .nav-item ::deep a:hover { background-color: #545454; color: white; } .sub-menu { display: none; flex-direction: column; padding-left: 0.5rem; } .nav-item:hover .sub-menu, .sub-menu:hover { display: flex; } .sub-menu .nav-link { font-size: 0.8rem; } @media (min-width: 641px) { .navbar-toggler { display: none; } .collapse { /* Never collapse the sidebar for wide screens */ display: block; transition: width 1s ease; min-width: 300px; } } @media (max-width: 640px) { .bottom-row { display: block; } }