mica
mica

Example

Surface treatment
Preview width

Try the panel button, nested Projects links, workspace switcher, and account control. Desktop icon collapse is explicitly enabled in this example. On mobile, both treatments use the same flush panel with a compact close control. Icons are authored SVG examples, not a dependency or library requirement.

Open docked example · Open inset example

Basic usage — no icons required

This text-only example stays expanded on desktop and becomes a modal panel on narrow containers. Keep the dialog empty and closed in the source: the enhancement moves the existing sidebar into it.

<script type="module" src="sidebar.js"></script>
<m-sidebar-layout variant="docked">
  <m-sidebar id="app-sidebar">
    <header role="presentation">
      <a data-sidebar-item href="/">Your app</a>
      <button type="button" data-sidebar-close aria-label="Close navigation">×</button>
    </header>
    <nav aria-label="Workspace">
      <section>
        <h2>Workspace</h2>
        <a data-sidebar-item href="/overview" aria-current="page">Overview</a>
        <a data-sidebar-item href="/projects">Projects</a>
      </section>
    </nav>
    <footer role="presentation"><a data-sidebar-item href="/account">Account</a></footer>
  </m-sidebar>
  <div data-sidebar-main>
    <header>
      <button type="button" data-sidebar-toggle
              aria-controls="app-sidebar" aria-label="Toggle navigation">Navigation</button>
    </header>
    <main>…page content…</main>
  </div>
  <dialog data-sidebar-dialog aria-label="Workspace navigation"></dialog>
</m-sidebar-layout>

API

Layout attributes

NameDefaultDescription
variant="docked | inset"dockedOn m-sidebar-layout. Docked separates panel and content with one edge. Inset puts a thin square frame around the content. Both become flush on mobile.
collapse="icon"omittedOpt-in desktop icon rail. Without it, the desktop panel stays expanded and the trigger appears only on mobile. Requires a named icon or avatar on each top-level item. Invalid anatomy keeps the panel expanded and the desktop toggle visible but unavailable, with an explanatory title and console diagnostic. Mobile navigation remains available.
persist="false"enabledOpt out of reading and writing the desktop collapse preference cookie.
aria-current="page"Application-owned state on the current link. Stronger text and a quiet background, with no border or shadow.

Content/markup contract

PartContractDescription
PanelDirect m-sidebar, first childUnique ID. Contains optional header/footer and a named native nav. Header/footer stay fixed; navigation scrolls. The panel itself is inert unless used in an enhanced layout.
Main surfaceDirect [data-sidebar-main]Consumer-authored toolbar and main content. Scrolls independently inside the layout's height.
Mobile dialogDirect, empty dialog[data-sidebar-dialog]Give it an accessible name. The same authored panel is moved here on mobile, then restored on desktop. Do not replace the required children after enhancement.
Togglebutton[data-sidebar-toggle] in the main surfaceUse type="button", a stable accessible label, and aria-controls matching the panel ID. The module manages aria-expanded. Multiple matching toggles are supported.
Closebutton[data-sidebar-close] in the panel headerAuthor the content and accessible name. Visible beside the workspace control only on mobile.
Item[data-sidebar-item] on a native link, button, or summaryShared spacing and presentation. Navigation remains ordinary links; controls retain native behavior.
Groupnav > section > h2Optional section label. Label each nav region to distinguish it from other navigation.
Nested linksdetails > summary[data-sidebar-item] and [data-sidebar-subnav]Use a subnav wrapper after summary. Direct anchors get the nested style. Clicking a collapsed rail summary expands the panel and opens that details group.

An m-avatar inside a named sidebar item counts as its collapsed visual without data-sidebar-icon. Set --avatar-size on the avatar to size it; keep the accompanying text in data-sidebar-label.

Optional icon-rail anatomy

AttributeTargetDescription
data-sidebar-iconAuthored icon, image, or text markOnly sizes and aligns the supplied content. Mica ships no sidebar icon set. Use aria-hidden="true" for decorative icons.
data-sidebar-labelItem's text wrapperHidden in icon mode. Every top-level item must have its own aria-label or aria-labelledby and either a data-sidebar-icon visual or m-avatar outside its hidden label to enable the rail. A title can supplement discoverability.
data-sidebar-badgeOptional count or metadataAligned at the end; hidden in icon mode.
data-sidebar-chevronOptional authored disclosure indicatorHidden in icon mode. A direct summary indicator rotates when its native details opens.

Styling

Set these properties on m-sidebar-layout. Semantic surface, text, and border colors come from the normal mica theme.

PropertyDefaultDescription
--sidebar-width15.5remExpanded desktop panel width.
--sidebar-rail-width3.75remCollapsed desktop width.
--sidebar-mobile-width18remMobile dialog width, clamped to leave an outside-dismiss area.
--sidebar-height100svhLayout height. Set a bounded height for an embedded workspace.
--sidebar-padvar(--space-xs)Panel padding; mobile safe-area insets are respected.
--sidebar-icon-size1remDefault icon box. Avatars retain their own --avatar-size, including when marked with data-sidebar-icon.
--sidebar-font-size0.875remItem text.
--sidebar-group-size0.6875remGroup label text.
--sidebar-subnav-size0.8125remNested link and metadata text.
--sidebar-active-weight600Current-link text weight.
--sidebar-close-size2.75remMobile close target.

Enhancement and accessibility

Import sidebar.js to register m-sidebar-layout. The module enhances valid authored markup, adds no icons or links, and has no shared runtime. Below 44rem layout width, it uses a native modal dialog for focus containment and Escape dismissal. Clicking outside or following an ordinary unprevented link also closes the panel. Modified clicks keep their normal behavior.

Desktop collapse state is remembered automatically in a cookie named mica-sidebar- followed by the URL-encoded m-sidebar ID. Keep that ID stable across pages to share the preference; use different IDs for independent sidebars. The cookie stores collapsed or expanded, lasts one year from the last change, and applies across the origin (Path=/, SameSite=Lax, and Secure on HTTPS). Restoration happens when the module connects. Mobile drawers still start closed, and opening or closing them does not change the desktop preference. Invalid collapse anatomy stays expanded without overwriting the cookie. If cookies are unavailable, the sidebar continues to work without persistence.

Set persist="false" on m-sidebar-layout to disable persistence. This leaves any existing cookie untouched; it will be read again on a future connection with persistence enabled.

The layout exposes open(), close(), and toggle(). On mobile they control the dialog; on desktop they expand/collapse the optional rail. Without icon collapse, desktop close() and toggle() leave the panel expanded. Client-side routers that prevent default link navigation can call layout.close() after routing. Mica does not choose the active page or emit custom navigation events.

Without the import, the sidebar remains visible, native links and details work, and inert toggle/close buttons stay hidden. At narrow viewport widths the unenhanced layout stacks its sidebar above content. Missing required controls or dialog anatomy also leaves this fallback intact.

Use a native page header for the banner. Panel header/footer are internal grouping: role="presentation" avoids duplicate page landmarks when they are not nested in a sectioning element. Do not add application-menu roles to navigation links. Keep popovers owned by sidebar controls inside m-sidebar so they move with it and remain usable inside the mobile modal.

← bottom navigationdata table →