Docs
Sidebar elements

Sidebar elements

Utilities for building the sidebar such as menus and links

Structure

A sidebar is composed of the following structure:

  • SidebarContainer - Controls the view of the sidebar based on its width.
    • SidebarGroup (optional) - For building group of menus
      • SidebarGroupLabel (optional) - The label of the group
      • SidebarMenu - A list of menus
        • SidebarMenuItem - A list item of a menu
          • SidebarMenuButton - A menu button or a menu link
            • SidebarIcon - An icon of the menu
            • SidebarText - A text of the menu (hidden when SidebarContainer is small)
    • SidebarRail - An element to collapse the edge sidebar
Overview
Workspace

Building a sidebar

A sidebar requires .jun-sidebarContainer to function properly:

<div className="jun-sidebarContainer">...sidebar elements</div>

For a single menu, wrap button or a with a class .jun-sidebarMenuButton within .jun-sidebarMenu:

<button className="jun-sidebarMenuButton">
  <Icon className="jun-sidebarIcon" />
  <span className="jun-sidebarText">A menu text</span>
</button>

List of menus

Use .jun-sidebarMenu to create a list of menus, each menu button should be wrapped with .jun-sidebarMenuItem:

<ul className="jun-sidebarMenu">
  <li className="jun-sidebarMenuItem">
    <button className="jun-sidebarMenuButton">
      <Icon className="jun-sidebarIcon" />
      <span className="jun-sidebarText">A menu text</span>
    </button>
  </li>
  ...
  <li className="jun-sidebarMenuItem">...</li>
</ul>

Group of menus

Use .jun-sidebarGroup for a group of menu, each group can have a label using .jun-sidebarGroupLabel:

<div className="jun-sidebarGroup">
  <div className="jun-sidebarGroupLabel">Group 1</div>
  <ul className="jun-sidebarMenu">...list of menus</ul>
</div>
 
<div className="jun-sidebarGroup">
  <div className="jun-sidebarGroupLabel">Group 2</div>
  <ul className="jun-sidebarMenu">...list of menus</ul>
</div>

Group of texts

If the menu has more than one text elements, wrap those texts with .jun-sidebarGroupText. The layout will collapse the texts when EdgeSidebar is in collapsed state.

You can control the spacing of the menu button using a modifier .jun-sidebarMenuButton-spacing-[*].

<a className="jun-sidebarMenuButton jun-sidebarMenuButton-spacing-3.5">
  <svg className="jun-sidebarIcon" />
  <div className="jun-sidebarGroupText">
    <div>
      <span className="jun-sidebarText">Primary text</span>
      <span className="jun-sidebarText">Secondary text</span>
      <span className="jun-sidebarText">Tertiary text</span>
    </div>
  </div>
</a>

To add a secondary action to a menu, create a button with .jun-sidebarMenuAction:

<li className="jun-sidebarMenuItem">
  <a className="jun-sidebarMenuButton">
    <svg className="jun-sidebarIcon" />
    <span className="jun-sidebarText">Dashboard</span>
  </a>
  <button className="jun-sidebarMenuAction">
    <MoreHorizontal />
  </button>
</li>

Append .jun-sidebarMenuAction-hoverAppear, if you want the action to appear when users hover on the item. The action also appears on keyboard focus.

Tooltip

To show a tooltip (using radix or shadcn) only when the permanent EdgeSidebar is collapsed, first wraps the layout with the TooltipProvider:

<TooltipProvider delayDuration={0}>
  <div className="jun-layout">...</div>
</TooltipProvider>

Then, create a TooltipSidebar that wraps TooltipPrimitive.Portal and TooltipPrimitive.Content to custom the container prop to be the EdgeSidebar element:

import * as TooltipPrimitive from "@radix-ui/react-tooltip";
 
function TooltipSidebar({ children }) {
  const [container, setContainer] = React.useState(null);
  React.useEffect(() => {
    setContainer(document.querySelector(".jun-edgeSidebar"));
  }, []);
  return (
    <TooltipPrimitive.Portal container={container}>
      <TooltipPrimitive.Content
        ref={ref}
        sideOffset={4}
        side="right"
        className={cn(
          "z-50 overflow-hidden rounded-md bg-primary px-3 py-1.5 text-xs text-primary-foreground animate-in fade-in-0 zoom-in-95",
          className
        )}
      >
        {children}
      </TooltipPrimitive.Content>
    </TooltipPrimitive.Portal>
  );
}

Finally, create a tooltip for each menu and apply .jun-sidebarTooltip to the TooltipContent:

<li className="jun-sidebarMenuItem">
  <Tooltip>
    <TooltipTrigger>
      <a className="jun-sidebarMenuButton">
        <svg className="jun-sidebarIcon" />
        <span className="jun-sidebarText">Dashboard</span>
      </a>
    </TooltipTrigger>
    <TooltipContent side="right" align="center" className="jun-sidebarTooltip">
      <p>Dashboard</p>
    </TooltipContent>
  </Tooltip>
</li>

Nested menu

The nested menu should be a direct child of .jun-sidebarMenuItem to follow semantic structure, then append .jun-sidebarMenu-nested to the menu. The .jun-sidebarGroupText is used to hide the nested menu when the sidebar is collapsed.

<ul className="jun-sidebarMenu">
  <li key={itemIndex} className="jun-sidebarMenuItem">
    <button className="jun-sidebarMenuButton">
      <Icon className="jun-sidebarIcon jun-sidebarIcon-shrink-size-5" />
      <span className="jun-sidebarText">{item.label}</span>
    </button>
 
    <div className="jun-sidebarGroupText">
      <div>
        <ul className="jun-sidebarMenu jun-sidebarMenu-nested">
          <li className="jun-sidebarMenuItem">
            <button className="jun-sidebarMenuButton">
              <span className="jun-sidebarText">{sub.label}</span>
            </button>
          </li>
        </ul>
      </div>
    </div>
  </li>
</ul>

Collapsible menu

To create a collapsible menu, first change the menu button tag to label and add a hidden checkbox like this (id and htmlFor are required):

- <button className="jun-sidebarMenuButton">
+ <label
+  htmlFor={`menu-${item.label}`}
+  className="jun-sidebarMenuButton jun-collapsibleTrigger"
+ >
  <Icon className="jun-sidebarIcon jun-sidebarIcon-shrink-size-5" />
  <span className="jun-sidebarText">{item.label}</span>
+  <input
+    type="checkbox"
+    className="sr-only"
+    id={`menu-${item.label}`}
+  />
+ </label>
- </button>

Then, create a div with .jun-collapsibleContent as a direct child of menu item, add another div as a child and then the nested menu:

<li key={itemIndex} className="jun-sidebarMenuItem">
  <label
    htmlFor={`menu-${item.label}`}
    className="jun-sidebarMenuButton jun-collapsibleTrigger"
  >
    ...
  </label>
 
  <div className="jun-collapsibleContent">
    <div>
      <ul className="jun-sidebarMenu jun-sidebarMenu-nested">
        {item.items.map((sub) => (
          <li className="jun-sidebarMenuItem">
            <button className="jun-sidebarMenuButton">
              <span className="jun-sidebarText">{sub.label}</span>
            </button>
          </li>
        ))}
      </ul>
    </div>
  </div>
</li>

Finally, an optional .jun-collapsibleIcon can be used as an indicator.

<label
  htmlFor={`menu-${item.label}`}
  className="jun-sidebarMenuButton jun-collapsibleTrigger"
>
  <Icon className="jun-sidebarIcon jun-sidebarIcon-shrink-size-5" />
  <span className="jun-sidebarText">{item.label}</span>
  <ChevronDown className="size-4 jun-collapsibleIcon jun-collapsibleIcon-rotate-180" />
  <input
    type="checkbox"
    className="sr-only"
    id={`menu-${item.label}`}
    defaultChecked
  />
</label>

Collapsible menu action

If the menu is an anchor, moved the collapsible trigger to the menu action instead.

The order must strictly follow:

<li className="jun-sidebarMenuItem">
  <button className="jun-sidebarMenuButton">...</button>
 
  {/* Collapsible trigger as secondary action */}
  <label
    htmlFor="menu-orders"
    className="jun-sidebarMenuAction jun-collapsibleTrigger"
  >
    <ChevronDown className="size-4 jun-collapsibleIcon jun-collapsibleIcon-rotate-180" />
    <input
      type="checkbox"
      className="sr-only"
      id="menu-orders"
      defaultChecked
    />
  </label>
 
  {/* Nested menu wrapped in collapsible content */}
  <div className="jun-collapsibleContent">...</div>
</li>

Examples

Flat menus

<div className="jun-edgeContent bg-sidebar">
  <div className="jun-sidebarContainer">
    <ul className="jun-sidebarMenu">
      <li className="jun-sidebarMenuItem">
        <button className="jun-sidebarMenuButton">
          <Home className="jun-sidebarIcon jun-sidebarIcon-shrink-size-5" />
          <span className="jun-sidebarText">Home</span>
        </button>
      </li>
      ...items
    </ul>
  </div>
</div>

Group menus

<div className="jun-sidebarContainer">
  <div className="jun-sidebarGroup">
    <div className="jun-sidebarGroupLabel">Main</div>
    <ul className="jun-sidebarMenu">
      <li className="jun-sidebarMenuItem">
        <button className="jun-sidebarMenuButton">
          <Home className="jun-sidebarIcon jun-sidebarIcon-shrink-size-5" />
          <span className="jun-sidebarText">Home</span>
        </button>
      </li>
      <li className="jun-sidebarMenuItem">
        <button className="jun-sidebarMenuButton">
          <Users className="jun-sidebarIcon jun-sidebarIcon-shrink-size-5" />
          <span className="jun-sidebarText">Users</span>
        </button>
      </li>
    </ul>
  </div>
  ...groups
</div>

Collapsible menus

<div className="jun-sidebarContainer">
  <ul className="jun-sidebarMenu">
    <li className="jun-sidebarMenuItem">
      <label
        htmlFor="menu-home"
        className="jun-sidebarMenuButton jun-collapsibleTrigger"
      >
        <Home className="jun-sidebarIcon jun-sidebarIcon-shrink-size-5" />
        <span className="jun-sidebarText">Home</span>
        <ChevronDown className="size-4 jun-collapsibleIcon jun-collapsibleIcon-rotate-180" />
        <input
          type="checkbox"
          className="sr-only"
          id="menu-home"
          defaultChecked
        />
      </label>
 
      <div className="jun-collapsibleContent">
        <div>
          <ul className="jun-sidebarMenu jun-sidebarMenu-nested">
            <li className="jun-sidebarMenuItem">
              <button className="jun-sidebarMenuButton">
                <Users className="jun-sidebarIcon jun-sidebarIcon-shrink-size-5" />
                <span className="jun-sidebarText">Users</span>
              </button>
            </li>
            <li className="jun-sidebarMenuItem">
              <button className="jun-sidebarMenuButton">
                <FileText className="jun-sidebarIcon jun-sidebarIcon-shrink-size-5" />
                <span className="jun-sidebarText">Documents</span>
              </button>
            </li>
            <li className="jun-sidebarMenuItem">
              <button className="jun-sidebarMenuButton">
                <Settings className="jun-sidebarIcon jun-sidebarIcon-shrink-size-5" />
                <span className="jun-sidebarText">Settings</span>
              </button>
            </li>
          </ul>
        </div>
      </div>
    </li>
    ...collapsible items
  </ul>
</div>

Collapsible menus (secondary action)

<div className="jun-sidebarContainer">
  <ul className="jun-sidebarMenu">
    <li className="jun-sidebarMenuItem">
      <button className="jun-sidebarMenuButton">
        <Home className="jun-sidebarIcon jun-sidebarIcon-shrink-size-5" />
        <span className="jun-sidebarText">Home</span>
      </button>
 
      <label className="jun-sidebarMenuAction jun-collapsibleTrigger">
        <ChevronDown className="size-4 jun-collapsibleIcon jun-collapsibleIcon-rotate-180" />
        <input
          type="checkbox"
          className="sr-only"
          id="menu-home"
          defaultChecked
        />
      </label>
 
      <div className="jun-collapsibleContent">
        <div>
          <ul className="jun-sidebarMenu jun-sidebarMenu-nested">
            <li className="jun-sidebarMenuItem">
              <button className="jun-sidebarMenuButton">
                <Users className="jun-sidebarIcon jun-sidebarIcon-shrink-size-5" />
                <span className="jun-sidebarText">Users</span>
              </button>
              <button className="jun-sidebarMenuAction jun-sidebarMenuAction-hoverAppear">
                <MoreVertical />
              </button>
            </li>
            <li className="jun-sidebarMenuItem">
              <button className="jun-sidebarMenuButton">
                <FileText className="jun-sidebarIcon jun-sidebarIcon-shrink-size-5" />
                <span className="jun-sidebarText">Documents</span>
              </button>
              <button className="jun-sidebarMenuAction jun-sidebarMenuAction-hoverAppear">
                <MoreVertical />
              </button>
            </li>
            <li className="jun-sidebarMenuItem">
              <button className="jun-sidebarMenuButton">
                <Settings className="jun-sidebarIcon jun-sidebarIcon-shrink-size-5" />
                <span className="jun-sidebarText">Settings</span>
              </button>
              <button className="jun-sidebarMenuAction jun-sidebarMenuAction-hoverAppear">
                <MoreVertical />
              </button>
            </li>
          </ul>
        </div>
      </div>
    </li>
  </ul>
</div>