Sidebar

A composable, themeable and customizable sidebar component.

Installation

npx nx g @spartan-ng/cli:ui sidebar

Add the following colors to your CSS file

The command above should install the colors for you. If not, copy and paste the following in your CSS file.
We'll go over the colors later in the theming section.

@layer base {
  :root {
       --sidebar: 300 0% 98%;
        --sidebar-foreground: 300 0% 4%;
        --sidebar-primary: 330 0% 9%;
        --sidebar-primary-foreground: 300 0% 98%;
        --sidebar-accent: 300 0% 96%;
        --sidebar-accent-foreground: 330 0% 9%;
        --sidebar-border: 330 0% 90%;
        --sidebar-ring: 0 0% 63%;
  }

  .dark {
    --sidebar: 300 0% 98%;
        --sidebar-foreground: 300 0% 4%;
        --sidebar-primary: 330 0% 9%;
        --sidebar-primary-foreground: 300 0% 98%;
        --sidebar-accent: 300 0% 96%;
        --sidebar-accent-foreground: 330 0% 9%;
        --sidebar-border: 330 0% 90%;
        --sidebar-ring: 0 0% 63%;
  }
}

Structure

A Sidebar component is composed of the following parts:
  • HlmSidebarService - Handles collapsible state.
  • HlmSidebar - The sidebar container.
  • HlmSidebarHeader and HlmSidebarFooter - Sticky at the top and bottom of the sidebar.
  • HlmSidebarContent - Scrollable content.
  • HlmSidebarGroup - Section within the HlmSidebarContent .
  • HlmSidebarTrigger - Trigger for the HlmSidebar .
Sidebar structure

Usage

src/app/app.ts
import { Component } from '@angular/core';
import { AppSidebar } from './app-sidebar';
import { HlmSidebarImports } from '@spartan-ng/helm/sidebar';

@Component({
  imports: [AppSidebar, HlmSidebarImports],
  selector: 'app-root',
  template: `<app-sidebar>
    <main>
      <button hlmSidebarTrigger><span class="sr-only"></span></button>
    </main>
  </app-sidebar>`,
})
export class App {}
src/app/app-sidebar.ts
import { Component } from '@angular/core';
import { HlmSidebarImports } from '@spartan-ng/helm/sidebar';

@Component({
  selector: 'app-sidebar',
  imports: [
    HlmSidebarImports,
  ],
  template: `
    <div hlmSidebarWrapper>
      <hlm-sidebar>
        <div hlmSidebarHeader></div>
        <div hlmSidebarContent>
          <div hlmSidebarGroup></div>
          <div hlmSidebarGroup></div>
        </div>
        <div hlmSidebarFooter></div>
      </hlm-sidebar>
      <ng-content />
    </div>
  `,
})
export class AppSidebar {}

Your First Sidebar

Let's start with the most basic sidebar. A collapsible sidebar with a menu.

Add a HlmSidebarTrigger at the root of your application.

src/app/app.ts
import { Component } from '@angular/core';
import { AppSidebar } from './app-sidebar';
import { HlmSidebarImports } from '@spartan-ng/helm/sidebar';
import { RouterOutlet } from '@angular/router';

@Component({
  imports: [AppSidebar, HlmSidebarImports, RouterOutlet],
  selector: 'app-root',
  template: `<app-sidebar>
    <main hlmSidebarInset>
      <header class="flex h-12 items-center justify-between px-4">
        <button hlmSidebarTrigger><span class="sr-only"></span></button>
        <router-outlet/>
      </header>
    </main>
  </app-sidebar>`,
})
export class App {}

Create a new sidebar component at src/app/app-sidebar.ts

src/app/app-sidebar.ts
import { Component } from '@angular/core';
import { HlmSidebarImports } from '@spartan-ng/helm/sidebar';

@Component({
  selector: 'app-sidebar',
  imports: [HlmSidebarImports],
  template: `
    <div hlmSidebarWrapper>
      <hlm-sidebar>
        <div hlmSidebarContent></div>
      </hlm-sidebar>
      <ng-content />
    </div>
  `,
})
export class AppSidebar {}

Now, let's add a HlmSidebarMenu to the sidebar.

We'll use the HlmSidebarMenu component in a HlmSidebarGroup .

src/app/app-sidebar.ts
import { Component } from '@angular/core';
import { HlmSidebarImports } from '@spartan-ng/helm/sidebar';
import { lucideCalendar, lucideHouse, lucideInbox, lucideSearch, lucideSettings } from '@ng-icons/lucide';
import { NgIcon, provideIcons } from '@ng-icons/core';
import { HlmIcon } from '@spartan-ng/helm/icon';

@Component({
  selector: 'app-sidebar',
  imports: [
    HlmSidebarImports,
    NgIcon,
    HlmIcon,
  ],
  template: `
    <div hlmSidebarWrapper>
      <hlm-sidebar>
        <div hlmSidebarContent>
          <div hlmSidebarGroup>
            <div hlmSidebarGroupLabel>Application</div>
            <div hlmSidebarGroupContent>
              <ul hlmSidebarMenu>
                @for(item of _items; track item.title){
                <li hlmSidebarMenuItem>
                  <a hlmSidebarMenuButton>
                    <ng-icon hlm [name]="item.icon" />
                    <span>{{ item.title }}</span>
                  </a>
                </li>
                }
              </ul>
            </div>
          </div>
        </div>
      </hlm-sidebar>
      <ng-content />
    </div>
  `,
  providers: [
    provideIcons({
      lucideHouse,
      lucideInbox,
      lucideCalendar,
      lucideSearch,
      lucideSettings,
    }),
  ],
})
export class AppSidebar {
  protected readonly _items = [
    {
      title: 'Home',
      url: '#',
      icon: 'lucideHouse',
    },
    {
      title: 'Inbox',
      url: '#',
      icon: 'lucideInbox',
    },
    {
      title: 'Calendar',
      url: '#',
      icon: 'lucideCalendar',
    },
    {
      title: 'Search',
      url: '#',
      icon: 'lucideSearch',
    },
    {
      title: 'Settings',
      url: '#',
      icon: 'lucideSettings',
    },
  ];
}

You've created your first sidebar.

You should see something like this:

First sidebar

SidebarService

The HlmSidebarService manages the state, persistence, and responsive behavior of the sidebar. It ensures the sidebar works consistently across devices, remembers user preferences, and provides developer APIs for customization.

Core Responsibilities

  • State Management – Tracks whether the sidebar is expanded or collapsed.
  • Mobile Responsiveness – Detects screen size and manages a separate mobile overlay state.
  • Persistence – Stores sidebar state in cookies for 7 days.
  • Variants – Supports sidebar , floating , and inset display styles.
  • Keyboard Shortcuts – Toggle with Ctrl / ⌘ + B .
  • Cleanup – Removes event listeners on destroy.

Signals & Computed

  • open – Whether the desktop sidebar is open.
  • openMobile – Whether the mobile sidebar is open.
  • isMobile – Whether the viewport is below 768px.
  • variant – Current sidebar style ( sidebar , floating , or inset ).
  • state – Computed: 'expanded' or 'collapsed' .

Public API

  • setOpen(open: boolean) – Opens or closes the desktop sidebar (persists state in a cookie).
  • setOpenMobile(open: boolean) – Opens/closes the mobile sidebar.
  • setVariant(variant) – Sets the sidebar style.
  • toggleSidebar() – Toggles the sidebar depending on desktop/mobile mode.

Keyboard Shortcut

The SIDEBAR_KEYBOARD_SHORTCUT variable defines the keyboard shortcut used to open and close the sidebar.

  • On macOS, use ⌘ + B to toggle the sidebar.
  • On Windows/Linux, use Ctrl + B to toggle the sidebar.

To change the shortcut, update the value of SIDEBAR_KEYBOARD_SHORTCUT in the HlmSidebarService source. By default it is set to 'b' .

Width

The HlmSidebarWrapper directive controls the width of the sidebar and its icon-only collapsed state . It applies styles, sets CSS variables, and ensures consistent layout across different sidebar variants.

Inputs

  • [class] – Pass custom classes to extend styling.
  • [sidebarWidth] – Override default sidebar width (e.g. 280px ).
  • [sidebarWidthIcon] – Override collapsed width (e.g. 72px ).

Defaults

  • sidebarWidth – 16rem
  • sidebarWidthMobile – 18rem
  • sidebarWidthIcon – 3rem
  • sidebarCookieName – sidebar_state
  • sidebarCookieMaxAge – 60 * 60 * 24 * 7
  • sidebarKeyboardShortcut – b
  • mobileBreakpoint – 768px

Config

To override defaults, provide a custom configuration in your application config using provideHlmSidebarConfig .

src/app/app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideHlmSidebarConfig } from '@spartan-ng/helm/sidebar';

export const appConfig: ApplicationConfig = {
    providers: [
        provideHlmSidebarConfig({
            ...
            sidebarWidth: '16rem',
            sidebarWidthMobile: '18rem',
            sidebarWidthIcon: '3rem',
            sidebarCookieName: 'sidebar_state',
            sidebarCookieMaxAge: 60 * 60 * 24 * 7,
            sidebarKeyboardShortcut: 'b',
            mobileBreakpoint: '768px',
        }),
    ],
};

HlmSidebarHeader

Use the HlmSidebarHeader component to add a sticky header at the top of your sidebar. This is useful for branding, application titles, or quick-access navigation.

sidebar-header

HlmSidebarFooter

Use the HlmSidebarFooter component to add a sticky footer at the bottom of your sidebar. This is useful for secondary actions, user profile information, or app settings.

sidebar-footer

HlmSidebarContent

The HlmSidebarContent component is used to wrap the main content of the sidebar. This is where you add your HlmSidebarGroup components, navigation links, or menus. The content inside is scrollable , while the header and footer remain sticky.

src/app/app-sidebar.ts
import { Component } from '@angular/core';
import { HlmSidebarImports } from '@spartan-ng/helm/sidebar';

@Component({
  selector: 'app-sidebar',
  imports: [HlmSidebarImports],
  template: `
    <div hlmSidebarWrapper>
      <hlm-sidebar>
        <div hlmSidebarContent>
          <div hlmSidebarGroup></div>
          <div hlmSidebarGroup></div>
        </div>
      </hlm-sidebar>
      <ng-content />
    </div>
  `,
})
export class AppSidebar {}

HlmSidebarGroup

Use the HlmSidebarGroup component to create a section within the sidebar. A SidebarGroup is composed of:

  • HlmSidebarGroupLabel – the section label or title.
  • HlmSidebarGroupContent – the main body of the group, usually containing links or menu items.
  • HlmSidebarGroupAction (optional) – a button or action element associated with the group, e.g. "Add" or "Settings".
sidebar-group

Collapsable HlmSidebarGroup

To make a HlmSidebarGroup collapsible, wrap it in a BrnCollapsible

sidebar-collapsable

HlmSidebarGroupAction

Use the HlmSidebarGroupAction component to add an action button to the HlmSidebarGroup .

sidebar-group-action

HlmSidebarMenu

The HlmSidebarMenu component is used for building a menu inside a HlmSidebarGroup . It is composed of the following parts:

  • HlmSidebarMenuItem – A single menu entry within the menu.
  • HlmSidebarMenuButton – A clickable button or link inside a menu item.
  • HlmSidebarMenuAction – An optional action (e.g., context menu, settings) for the menu item.
  • HlmSidebarMenuSub – A nested submenu within a menu item.
Sidebar menu

Here's an example of a HlmSidebarMenu component rendering a list of projects.

sidebar-menu

HlmSidebarMenuButton

The HlmSidebarMenuButton component is used to render a menu button within a HlmSidebarMenuItem .

<li hlmSidebarMenuItem>
  <a hlmSidebarMenuButton href="#">
    Home
  </a>
</li>

Button

<li hlmSidebarMenuItem>
  <button hlmSidebarMenuButton>
    Send
  </button>
</li>

Icon and Label

<li hlmSidebarMenuItem>
  <a hlmSidebarMenuButton href="#">
    <ng-icon hlm name="lucideHouse" />
    <span>Home</span>
  </a>
</li>

You can render an icon and a truncated label inside the button. Remember to wrap the label in a .

isActive

Use the isActive prop to mark a menu item as active.

<li hlmSidebarMenuItem>
  <a hlmSidebarMenuButton href="#" isActive>
    Home
  </a>
</li>

HlmSidebarMenuAction

The HlmSidebarMenuAction component is used to render a menu action within a HlmSidebarMenuItem .

This button works independently of the HlmSidebarMenuButton . For example, you can have a SidebarMenuButton as a clickable link, while the SidebarMenuAction provides a secondary action, such as editing, deleting, or opening a context menu.

<li hlmSidebarMenuItem>
  <a hlmSidebarMenuButton href="#">
    <ng-icon hlm name="lucideHouse" />
    <span>Home</span>
  </a>
  <button hlmSidebarMenuAction>
    <ng-icon hlm name="lucidePlus" />
    <span class="sr-only">Add Project</span>
  </button>
</li>

Here's an example of a HlmSidebarMenuAction component rendering a HlmMenu .

sidebar-dropdown-menu

HlmMenuSub

The HlmSidebarMenuSub component is used to render a submenu within a HlmSidebarMenu .

Use HlmSidebarMenuSubItem and HlmSidebarMenuSubButton to render a submenu item.

sidebar-menu-sub

Collapsable HlmSidebarSubMenu

To make a HlmSidebarMenu component collapsible, wrap it and the HlmSidebarMenuSub components in a BrnCollapsible .

sidebar-menu-sub-collapsable

HlmSidebarMenuBadge

The HlmSidebarMenuBadge component is used to render a badge within a HlmSidebarMenuItem .

sidebar-menu-badge

HlmSidebarMenuSkeleton

The HlmSidebarMenuSkeleton component is used to render a skeleton for a SidebarMenu. You can use this to show a loading state while fetching data.

<ul hlmSidebarMenu>
    @for (project of _projects; track project) {
        <li hlmSidebarMenuItem>
            <div hlmSidebarMenuSkeleton></div>
        </li>
    }
</ul>

HlmSidebarMenuSeparator

The HlmSidebarMenuSeparator component is used to render a separator within a Sidebar .

<hlm-sidebar>
  <div hlmSidebarContent>
    <div hlmSidebarGroup></div>
        <div hlmSidebarSeparator></div>
    <div hlmSidebarGroup></div>
  </div>
</hlm-sidebar>

Theming

We use dedicated CSS variables for theming the sidebar, separate from the rest of the application.

@layer base {
  :root {
       --sidebar: 300 0% 98%;
        --sidebar-foreground: 300 0% 4%;
        --sidebar-primary: 330 0% 9%;
        --sidebar-primary-foreground: 300 0% 98%;
        --sidebar-accent: 300 0% 96%;
        --sidebar-accent-foreground: 330 0% 9%;
        --sidebar-border: 330 0% 90%;
        --sidebar-ring: 0 0% 63%;
  }

  .dark {
    --sidebar: 300 0% 98%;
        --sidebar-foreground: 300 0% 4%;
        --sidebar-primary: 330 0% 9%;
        --sidebar-primary-foreground: 300 0% 98%;
        --sidebar-accent: 300 0% 96%;
        --sidebar-accent-foreground: 330 0% 9%;
        --sidebar-border: 330 0% 90%;
        --sidebar-ring: 0 0% 63%;
  }
}

We intentionally use different variables for the sidebar and the rest of the application to make it easy to have a sidebar that is styled differently from the rest of the application. Think a sidebar with a darker shade from the main application.

Responsive behavior

The sidebar is responsive by default. It collapses to a minimal state on smaller screens and expands on larger screens. This behavior can be customized by overriding the default CSS variables or wrapping in media queries.

Accessibility

The sidebar and its components follow WAI-ARIA best practices. Ensure you provide appropriate labels for buttons and landmarks to improve screen reader support.

Skeleton Sheet