PetalComponents.Sidebar (petal_components v4.15.2)

Copy Markdown View Source

The app-shell navigation sidebar: a fixed rail of grouped links that collapses to icons on desktop and takes over as a sheet on mobile.

This is the chrome, not the list. sidebar_item/1 is here for when you are writing the nav markup anyway, but when your nav already ships as data, PetalComponents.Menu.vertical_menu/1 renders the same list from plain maps and drops straight into the content area. The sidebar is the shell it sits in.

Five function components compose the shell:

Usage

<.sidebar_shell for="app-sidebar">
  <:sidebar>
    <.sidebar_nav id="app-sidebar" label="Main" collapsible="icon">
      <:header>
        <.icon name="hero-cube" class="w-6 h-6 shrink-0" />
        <span class="pc-sidebar__brand">Acme</span>
      </:header>

      <.sidebar_group label="Workspace">
        <.sidebar_item label="Dashboard" path="/" icon="hero-home" active />
        <.sidebar_item label="Inbox" path="/inbox" icon="hero-inbox" badge="12" />
      </.sidebar_group>

      <.sidebar_group label="Account" collapsible open>
        <.sidebar_item label="Settings" icon="hero-cog-6-tooth">
          <.sidebar_item label="Profile" path="/settings/profile" />
          <.sidebar_item label="Billing" path="/settings/billing" />
        </.sidebar_item>
      </.sidebar_group>

      <:footer>
        <.sidebar_item label="Sign out" path="/sign-out" icon="hero-arrow-left-start-on-rectangle" />
      </:footer>
    </.sidebar_nav>
  </:sidebar>

  <header class="flex items-center gap-3 p-4">
    <.sidebar_trigger for="app-sidebar" target="mobile" />
    <h1>Dashboard</h1>
  </header>
  <main class="p-4">Your page</main>
</.sidebar_shell>

Collapse and state

Collapse is client-side by design: sidebar_trigger/1 flips a data-collapsed attribute on the sidebar with Phoenix.LiveView.JS, and CSS does the rest. No round trip, no hook.

The server still owns the initial value through the collapsed attr, so the first paint is already correct and a live_redirect cannot flash the wrong state - the attribute is rendered, not read back from the client. To persist the choice across navigation, keep it in your own assign (or a session cookie you read in the plug pipeline) and pass it back in:

<.sidebar_nav id="app-sidebar" collapsed={@sidebar_collapsed}>

...then have the trigger tell the server as well as the DOM:

<.sidebar_trigger for="app-sidebar" on_click={JS.push("toggle_sidebar")} />

Responsive behaviour

Below the md breakpoint (768px) a collapsible="icon" or "offcanvas" sidebar leaves the flow entirely and becomes an off-canvas sheet, opened by a target="mobile" trigger. While the sheet is open the shell's content region is marked inert, Escape closes it, clicking the scrim closes it, and focus returns to the trigger. collapsible="none" stays put at every width.

Theming

The sidebar reads two custom properties, so apps can retune widths without touching pc-* internals:

.pc-sidebar { --pc-sidebar-width: 18rem; --pc-sidebar-icon-width: 4.5rem; }

The breakpoint is fixed at md in this version; CSS media queries cannot read custom properties.

Summary

Functions

Closes the mobile sheet for the sidebar with the given id and restores focus to its trigger.

Opens the mobile sheet for the sidebar with the given id.

A labelled run of items. Pass collapsible to make the label a disclosure toggle.

One navigation item: icon, label, optional badge, optional nested sub-items.

The sidebar itself: a <nav> landmark wrapped in the collapse/sheet machinery.

The app shell: a flex row holding the sidebar beside your page content.

The button that collapses the rail (target="collapse") or opens the mobile sheet (target="mobile").

Toggles the desktop collapsed state of the sidebar with the given id.

Functions

hide_sidebar(js \\ %JS{}, id)

Closes the mobile sheet for the sidebar with the given id and restores focus to its trigger.

show_sidebar(js \\ %JS{}, id)

Opens the mobile sheet for the sidebar with the given id.

Marks the shell's content region inert, locks body scroll and moves focus into the nav.

toggle_sidebar(js \\ %JS{}, id)

Toggles the desktop collapsed state of the sidebar with the given id.

Pure client-side: flips data-collapsed and lets CSS do the work.