{
  "$schema": "https://uipkge.dev/schema/registry-item.json",
  "name": "sidebar",
  "title": "Sidebar",
  "type": "registry:ui",
  "description": "Full-height app sidebar — collapsible to icons, with grouping, sub-grouping, and integrated search. The navigation surface for product apps with many sections.",
  "categories": [
    "navigation"
  ],
  "files": [
    {
      "path": "packages/elements-lit/src/components/sidebar/sidebar.ts",
      "content": "import { LitElement, css, html, isServer, nothing } from 'lit'\nimport { live } from 'lit/directives/live.js'\nimport { styleMap } from 'lit/directives/style-map.js'\nimport { PanelLeft } from 'lucide'\nimport { cn } from '../../lib/utils'\nimport { ThemeController } from '../../lib/theme'\nimport { icon } from '../../lib/icon'\nimport { tailwind } from '../../lib/styles'\nimport { sidebarMenuButtonVariants, type SidebarMenuButtonVariants } from './sidebar.variants'\n\n/* ------------------------------------------------------------------ */\n/* Constants (React's, verbatim)                                       */\n/* ------------------------------------------------------------------ */\n\nconst SIDEBAR_COOKIE_NAME = 'sidebar_state'\nconst SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7\nconst SIDEBAR_WIDTH = '16rem'\nconst SIDEBAR_WIDTH_ICON = '3rem'\nconst SIDEBAR_KEYBOARD_SHORTCUT = 'b'\nconst MOBILE_QUERY = '(max-width: 768px)'\n\nconst sidebarTailwind = tailwind\n// Every sidebar host generates no box: React's element is rendered inside the\n// shadow root and laid out directly by the parent (flex/grid/list), exactly\n// like React's DOM. React's `className` → `class=\"[&::part(base)]:…\"`.\nconst contents = css`\n  :host {\n    display: contents;\n  }\n`\n\n// Boolean props that default to true must accept the string \"false\".\nconst trueByDefault = {\n  fromAttribute: (v: string | null) => v !== null && v !== 'false',\n  toAttribute: (v: boolean) => (v ? '' : null),\n}\n\ntype Side = 'left' | 'right'\ntype Variant = 'sidebar' | 'floating' | 'inset'\ntype Collapsible = 'offcanvas' | 'icon' | 'none'\n\n// `[&>svg]` / `[&>span:last-child]` can't see slotted children, so the same\n// rules are repeated as `::slotted()` on the <slot> (as button.ts does).\nconst slottedIcon = '[&::slotted(svg)]:size-4 [&::slotted(svg)]:shrink-0'\n\n/* ------------------------------------------------------------------ */\n/* Base class: how a part reaches SidebarProvider's context            */\n/* ------------------------------------------------------------------ */\n\n/**\n * Every sidebar part (and <uip-sidebar> itself) finds its provider with\n * `closest('uip-sidebar-provider')` on connect and registers with it; the\n * provider calls `requestUpdate()` on every registered part when its state\n * changes (open / isMobile / openMobile) or when a <uip-sidebar>'s data\n * attributes change. That's React's `useSidebar()` context.\n *\n * React styles the parts through ancestor/sibling selectors on the Sidebar's\n * outer div (`group-data-[collapsible=icon]:…`, `peer-data-[variant=inset]:…`)\n * and on the menu item / menu button (`group/menu-item`, `peer/menu-button`).\n * Selectors don't cross shadow roots, so each part renders a *mirror* inside\n * its own shadow root: a `display: contents` wrapper (or a hidden sibling)\n * carrying the same classes and data attributes as React's ancestor/peer,\n * copied from the real <uip-sidebar> host. React's class strings then work\n * verbatim.\n */\nabstract class SidebarElement extends LitElement {\n  static styles = [sidebarTailwind, contents]\n\n  protected abstract slotName: string\n  /** The provider this part belongs to (React's `useSidebar()`). */\n  provider: UipSidebarProvider | null = null\n\n  constructor() {\n    super()\n    new ThemeController(this)\n  }\n\n  connectedCallback() {\n    super.connectedCallback()\n    this.setAttribute('data-uipkge', '')\n    this.setAttribute('data-slot', this.slotName)\n    this.connectProvider()\n  }\n\n  disconnectedCallback() {\n    super.disconnectedCallback()\n    this.provider?.parts.delete(this)\n    this.provider = null\n  }\n\n  private connectProvider() {\n    const p = this.closest('uip-sidebar-provider')\n    if (!p) return\n    if (p instanceof UipSidebarProvider) {\n      this.provider = p\n      p.parts.add(this)\n      this.requestUpdate()\n    } else {\n      customElements.whenDefined('uip-sidebar-provider').then(() => this.isConnected && this.connectProvider())\n    }\n  }\n\n  /** React's `useSidebar().state`. */\n  protected get sidebarState() {\n    return this.provider?.state ?? 'expanded'\n  }\n\n  /**\n   * React's `group` ancestor — the Sidebar's outer div — mirrored around\n   * `content`, with the real <uip-sidebar> host's data attributes (none when\n   * the sidebar is collapsible=\"none\" or mobile, as in React).\n   */\n  protected group(content: unknown, extraClass = '', item?: UipSidebarMenuItem | null) {\n    const s = this.closest('uip-sidebar')\n    const a = (n: string) => s?.getAttribute(n) ?? nothing\n    return html`<div\n      class=${cn('group contents', extraClass)}\n      data-state=${a('data-state')}\n      data-collapsible=${a('data-collapsible')}\n      data-variant=${a('data-variant')}\n      data-side=${a('data-side')}\n      ?data-hover=${!!item?.hover}\n      ?data-focus-within=${!!item?.focusWithin}\n    >\n      ${content}\n    </div>`\n  }\n\n  /** The menu item this part is a direct child of. */\n  protected get item(): UipSidebarMenuItem | null {\n    const p = this.parentElement\n    return p instanceof UipSidebarMenuItem ? p : null\n  }\n\n  /**\n   * React's `peer/menu-button` (the sibling SidebarMenuButton) mirrored as a\n   * hidden element before the action / badge.\n   */\n  protected menuButtonPeer() {\n    const b = this.item?.menuButton\n    if (!b) return nothing\n    return html`<div\n      hidden\n      class=\"peer/menu-button\"\n      data-size=${b.size}\n      data-active=${String(b.isActive)}\n      ?data-hover=${b.hovered}\n    ></div>`\n  }\n}\n\n/* ------------------------------------------------------------------ */\n/* SidebarProvider                                                     */\n/* ------------------------------------------------------------------ */\n\n/**\n * <uip-sidebar-provider> — React's SidebarProvider (context + the\n * `sidebar-wrapper` div + TooltipProvider delayDuration=0).\n *\n *   <uip-sidebar-provider>\n *     <uip-sidebar collapsible=\"icon\">…</uip-sidebar>\n *     <main>…<uip-sidebar-trigger></uip-sidebar-trigger></main>\n *   </uip-sidebar-provider>\n *\n * Props: `default-open` (default true; `default-open=\"false\"` starts\n * collapsed), `open` (the live state; set `.open` to control it).\n * Events: `open-change` (detail: { open }) — React's `onOpenChange`, fired by\n * the trigger, the rail and Cmd/Ctrl+B. Like React, every change writes the\n * `sidebar_state` cookie (7 days); like React it is never read back (a server\n * can read it to render `default-open`).\n *\n * API (React's `useSidebar()`): `state` ('expanded' | 'collapsed'), `open`,\n * `setOpen(open)`, `isMobile` (viewport ≤ 768px), `openMobile`,\n * `setOpenMobile(open)`, `toggleSidebar()`.\n *\n * `--sidebar-width` / `--sidebar-width-icon` are set as inline style on the\n * host (as React sets them on the wrapper) unless the consumer's `style`\n * already sets them. The host is `display: contents`; React's `className`\n * goes on `::part(base)` (the wrapper div).\n */\nexport class UipSidebarProvider extends LitElement {\n  static styles = [sidebarTailwind, contents]\n\n  static properties = {\n    open: { reflect: true, converter: trueByDefault },\n    defaultOpen: { attribute: 'default-open', converter: trueByDefault },\n    isMobile: { state: true },\n    openMobile: { state: true },\n  }\n\n  open?: boolean\n  defaultOpen = true\n  isMobile = false\n  openMobile = false\n\n  /** Registered parts; re-rendered whenever the context changes. */\n  readonly parts = new Set<LitElement>()\n  private mql?: MediaQueryList\n\n  constructor() {\n    super()\n    new ThemeController(this)\n  }\n\n  get state(): 'expanded' | 'collapsed' {\n    return this.open ? 'expanded' : 'collapsed'\n  }\n\n  connectedCallback() {\n    super.connectedCallback()\n    this.setAttribute('data-uipkge', '')\n    this.setAttribute('data-slot', 'sidebar-wrapper')\n    if (this.open === undefined) this.open = this.defaultOpen\n    if (isServer) return\n    if (!this.style.getPropertyValue('--sidebar-width')) this.style.setProperty('--sidebar-width', SIDEBAR_WIDTH)\n    if (!this.style.getPropertyValue('--sidebar-width-icon'))\n      this.style.setProperty('--sidebar-width-icon', SIDEBAR_WIDTH_ICON)\n    this.mql = matchMedia(MOBILE_QUERY)\n    this.mql.addEventListener('change', this.onMediaChange)\n    this.onMediaChange()\n    addEventListener('keydown', this.onKeyDown)\n  }\n\n  disconnectedCallback() {\n    super.disconnectedCallback()\n    this.mql?.removeEventListener('change', this.onMediaChange)\n    removeEventListener('keydown', this.onKeyDown)\n  }\n\n  private onMediaChange = () => {\n    this.isMobile = !!this.mql?.matches\n  }\n\n  private onKeyDown = (event: KeyboardEvent) => {\n    if (event.key === SIDEBAR_KEYBOARD_SHORTCUT && (event.metaKey || event.ctrlKey)) {\n      event.preventDefault()\n      this.toggleSidebar()\n    }\n  }\n\n  setOpen(value: boolean) {\n    this.open = value\n    // This sets the cookie to keep the sidebar state.\n    document.cookie = `${SIDEBAR_COOKIE_NAME}=${value}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`\n    this.dispatchEvent(new CustomEvent('open-change', { detail: { open: value }, bubbles: true, composed: true }))\n  }\n\n  setOpenMobile(value: boolean) {\n    this.openMobile = value\n  }\n\n  toggleSidebar() {\n    return this.isMobile ? this.setOpenMobile(!this.openMobile) : this.setOpen(!this.open)\n  }\n\n  /** Re-render the provider and every part (a sidebar's data attributes changed). */\n  notify(source?: LitElement) {\n    this.requestUpdate()\n    this.parts.forEach((p) => p !== source && p.requestUpdate())\n  }\n\n  protected updated(changed: Map<string, unknown>) {\n    if (changed.has('open') || changed.has('isMobile') || changed.has('openMobile')) {\n      this.parts.forEach((p) => p.requestUpdate())\n    }\n  }\n\n  render() {\n    // React's `has-data-[variant=inset]:` looks at the wrapper's descendants;\n    // slotted nodes aren't shadow-tree descendants, so mirror a match.\n    const hasInset = !isServer && !!this.querySelector('[data-variant=inset]')\n    return html`<div\n      part=\"base\"\n      data-uipkge=\"\"\n      data-slot=\"sidebar-wrapper\"\n      class=\"group/sidebar-wrapper has-data-[variant=inset]:bg-sidebar flex min-h-svh w-full\"\n    >\n      ${hasInset ? html`<i hidden data-variant=\"inset\"></i>` : nothing}<slot></slot>\n    </div>`\n  }\n}\n\n/* ------------------------------------------------------------------ */\n/* Sidebar                                                             */\n/* ------------------------------------------------------------------ */\n\n/**\n * <uip-sidebar> — React's Sidebar.\n *\n * Props: `side` ('left' | 'right'), `variant` ('sidebar' | 'floating' |\n * 'inset'), `collapsible` ('offcanvas' | 'icon' | 'none').\n *\n * Like React it renders one of three branches: `collapsible=\"none\"` → a plain\n * panel; viewport ≤ 768px → a <uip-sheet> (openMobile, 18rem wide, no close\n * button); otherwise the desktop gap + fixed panel. The host carries the\n * outer div's `data-state` / `data-collapsible` / `data-variant` /\n * `data-side` and the `group peer` classes, so light-DOM children can use\n * React's `group-data-[collapsible=icon]:hidden` from the page's Tailwind.\n *\n * Parts: `base` (React's className target: the fixed panel, or the plain\n * panel when collapsible=\"none\"), `root` (outer div), `gap`, `inner`\n * (`data-sidebar=\"sidebar\"`), `sheet` (mobile <uip-sheet>).\n */\nexport class UipSidebar extends SidebarElement {\n  static properties = {\n    side: { reflect: true },\n    variant: { reflect: true },\n    collapsible: { reflect: true },\n  }\n\n  protected slotName = 'sidebar'\n  side: Side = 'left'\n  variant: Variant = 'sidebar'\n  collapsible: Collapsible = 'offcanvas'\n  private signature = ''\n\n  private get branch(): 'none' | 'mobile' | 'desktop' {\n    if (this.collapsible === 'none') return 'none'\n    return this.provider?.isMobile ? 'mobile' : 'desktop'\n  }\n\n  protected willUpdate() {\n    const branch = this.branch\n    const desktop = branch === 'desktop'\n    const set = (n: string, v: string | null) => (v === null ? this.removeAttribute(n) : this.setAttribute(n, v))\n    const state = this.sidebarState\n    set('data-state', desktop ? state : null)\n    set('data-collapsible', desktop ? (state === 'collapsed' ? this.collapsible : '') : null)\n    set('data-variant', desktop ? this.variant : null)\n    set('data-side', desktop ? this.side : null)\n    set('data-sidebar', branch === 'mobile' ? 'sidebar' : null)\n    set('data-mobile', branch === 'mobile' ? 'true' : null)\n    this.classList.toggle('group', desktop)\n    this.classList.toggle('peer', desktop)\n  }\n\n  protected updated() {\n    const sig = ['data-state', 'data-collapsible', 'data-variant', 'data-side'].map((n) => this.getAttribute(n)).join('|')\n    if (sig !== this.signature) {\n      this.signature = sig\n      this.provider?.notify(this)\n    }\n  }\n\n  private onSheetOpenChange(e: CustomEvent<{ open: boolean }>) {\n    // The inner sheet's event is an implementation detail, not the provider's\n    // open-change.\n    e.stopPropagation()\n    this.provider?.setOpenMobile(e.detail.open)\n  }\n\n  render() {\n    const branch = this.branch\n    if (branch === 'none') {\n      return html`<div\n        part=\"base\"\n        data-uipkge=\"\"\n        data-slot=\"sidebar\"\n        class=\"bg-sidebar text-sidebar-foreground flex h-full w-(--sidebar-width) flex-col\"\n      >\n        <slot></slot>\n      </div>`\n    }\n\n    if (branch === 'mobile') {\n      // React: SheetContent className + `--sidebar-width: 18rem`; `[&>button]:hidden`\n      // hides the close button (show-close-button=false here).\n      return html`<uip-sheet\n        part=\"sheet\"\n        data-sidebar=\"sidebar\"\n        data-mobile=\"true\"\n        side=${this.side}\n        .open=${!!this.provider?.openMobile}\n        .showCloseButton=${false}\n        hide-header\n        heading=\"Sidebar\"\n        description=\"Displays the mobile sidebar.\"\n        class=\"[--sidebar-width:18rem] [&::part(content)]:bg-sidebar [&::part(content)]:text-sidebar-foreground [&::part(content)]:w-(--sidebar-width) [&::part(content)]:p-0\"\n        @open-change=${this.onSheetOpenChange}\n      >\n        <div part=\"inner\" class=\"flex h-full w-full flex-col\"><slot></slot></div>\n      </uip-sheet>`\n    }\n\n    const { side, variant, collapsible } = this\n    const state = this.sidebarState\n    return html`<div\n      part=\"root\"\n      class=\"group peer text-sidebar-foreground hidden md:block\"\n      data-uipkge=\"\"\n      data-slot=\"sidebar\"\n      data-state=${state}\n      data-collapsible=${state === 'collapsed' ? collapsible : ''}\n      data-variant=${variant}\n      data-side=${side}\n    >\n      <div\n        part=\"gap\"\n        class=${cn(\n          'relative w-(--sidebar-width) bg-transparent transition-[width] duration-200 ease-linear',\n          'group-data-[collapsible=offcanvas]:w-0',\n          'group-data-[side=right]:rotate-180',\n          variant === 'floating' || variant === 'inset'\n            ? 'group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4)))]'\n            : 'group-data-[collapsible=icon]:w-(--sidebar-width-icon)',\n        )}\n      ></div>\n      <div\n        part=\"base\"\n        class=${cn(\n          'fixed inset-y-0 z-10 hidden h-svh w-(--sidebar-width) transition-[left,right,width] duration-200 ease-linear md:flex',\n          side === 'left'\n            ? 'left-0 group-data-[collapsible=offcanvas]:left-[calc(var(--sidebar-width)*-1)]'\n            : 'right-0 group-data-[collapsible=offcanvas]:right-[calc(var(--sidebar-width)*-1)]',\n          variant === 'floating' || variant === 'inset'\n            ? 'p-2 group-data-[collapsible=icon]:w-[calc(var(--sidebar-width-icon)+(--spacing(4))+2px)]'\n            : 'group-data-[collapsible=icon]:w-(--sidebar-width-icon) group-data-[side=left]:border-r group-data-[side=right]:border-l',\n        )}\n      >\n        <div\n          part=\"inner\"\n          data-sidebar=\"sidebar\"\n          class=\"bg-sidebar group-data-[variant=floating]:border-sidebar-border flex h-full w-full flex-col group-data-[variant=floating]:rounded-lg group-data-[variant=floating]:border group-data-[variant=floating]:shadow-sm\"\n        >\n          <slot></slot>\n        </div>\n      </div>\n    </div>`\n  }\n}\n\n/* ------------------------------------------------------------------ */\n/* SidebarTrigger / SidebarRail                                        */\n/* ------------------------------------------------------------------ */\n\n/**\n * <uip-sidebar-trigger> — React's SidebarTrigger: a ghost icon <uip-button>\n * (PanelLeft + sr-only \"Toggle Sidebar\") that calls `toggleSidebar()`.\n * Must be inside the provider. React's className → `::part(base)` (the\n * <uip-button>).\n */\nexport class UipSidebarTrigger extends SidebarElement {\n  protected slotName = 'sidebar-trigger'\n\n  render() {\n    return html`<uip-button\n      part=\"base\"\n      data-sidebar=\"trigger\"\n      variant=\"ghost\"\n      size=\"icon\"\n      class=\"[&::part(base)]:focus-visible:ring-ring [&::part(base)]:h-7 [&::part(base)]:w-7 [&::part(base)]:focus-visible:ring-2 [&::part(base)]:focus-visible:outline-none\"\n      @click=${() => this.provider?.toggleSidebar()}\n      >${icon(PanelLeft, 'panel-left')}<span class=\"sr-only\">Toggle Sidebar</span></uip-button\n    >`\n  }\n}\n\n/** <uip-sidebar-rail> — React's SidebarRail: the thin toggle strip on the sidebar's edge. */\nexport class UipSidebarRail extends SidebarElement {\n  protected slotName = 'sidebar-rail'\n\n  render() {\n    return this.group(\n      html`<button\n        part=\"base\"\n        type=\"button\"\n        data-sidebar=\"rail\"\n        data-uipkge=\"\"\n        data-slot=\"sidebar-rail\"\n        aria-label=\"Toggle Sidebar\"\n        tabindex=\"-1\"\n        title=\"Toggle Sidebar\"\n        class=${cn(\n          'hover:after:bg-sidebar-border focus-visible:ring-ring absolute inset-y-0 z-20 hidden w-4 -translate-x-1/2 transition-colors duration-200 ease-linear group-data-[side=left]:-right-4 group-data-[side=right]:left-0 after:absolute after:inset-y-0 after:left-1/2 after:w-[2px] focus-visible:ring-2 focus-visible:outline-none sm:flex',\n          'in-data-[side=left]:cursor-w-resize in-data-[side=right]:cursor-e-resize',\n          '[[data-side=left][data-state=collapsed]_&]:cursor-e-resize [[data-side=right][data-state=collapsed]_&]:cursor-w-resize',\n          'hover:group-data-[collapsible=offcanvas]:bg-sidebar group-data-[collapsible=offcanvas]:translate-x-0 group-data-[collapsible=offcanvas]:after:left-full',\n          '[[data-side=left][data-collapsible=offcanvas]_&]:-right-2',\n          '[[data-side=right][data-collapsible=offcanvas]_&]:-left-2',\n        )}\n        @click=${() => this.provider?.toggleSidebar()}\n      ></button>`,\n    )\n  }\n}\n\n/* ------------------------------------------------------------------ */\n/* SidebarInset                                                        */\n/* ------------------------------------------------------------------ */\n\n/**\n * <uip-sidebar-inset> — React's SidebarInset (<main>). React's\n * `peer-data-[variant=inset]:…` reads the preceding Sidebar; the element\n * mirrors that sidebar's data attributes as a hidden `peer` sibling.\n */\nexport class UipSidebarInset extends SidebarElement {\n  protected slotName = 'sidebar-inset'\n\n  private get peerSidebar() {\n    let s = this.previousElementSibling\n    while (s && s.localName !== 'uip-sidebar') s = s.previousElementSibling\n    return s\n  }\n\n  render() {\n    const s = this.peerSidebar\n    const a = (n: string) => s?.getAttribute(n) ?? nothing\n    return html`<div\n        hidden\n        class=\"peer\"\n        data-state=${a('data-state')}\n        data-collapsible=${a('data-collapsible')}\n        data-variant=${a('data-variant')}\n        data-side=${a('data-side')}\n      ></div>\n      <main\n        part=\"base\"\n        data-uipkge=\"\"\n        data-slot=\"sidebar-inset\"\n        class=${cn(\n          // min-w-0 is load-bearing: a flex-1 child without it inherits min-width: auto,\n          // which means any single wide descendant (chart, table, code block) blows the\n          // inset's width past its grid track. The upstream shadcn-ui sidebar omits this.\n          'bg-background relative flex w-full min-w-0 flex-1 flex-col',\n          'md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2',\n        )}\n      >\n        <slot></slot>\n      </main>`\n  }\n}\n\n/* ------------------------------------------------------------------ */\n/* SidebarInput / SidebarSeparator                                     */\n/* ------------------------------------------------------------------ */\n\n/**\n * <uip-sidebar-input> — React's SidebarInput: a <uip-input> with\n * `bg-background h-8 w-full shadow-none`. Forwards `value`, `placeholder`,\n * `type`, `name`, `disabled`, `aria-label`; `input` / `change` bubble from the\n * inner input (read `.value`).\n */\nexport class UipSidebarInput extends SidebarElement {\n  static properties = {\n    value: {},\n    placeholder: {},\n    type: {},\n    name: { reflect: true },\n    disabled: { type: Boolean, reflect: true },\n    accessibleLabel: { attribute: 'aria-label' },\n  }\n\n  protected slotName = 'sidebar-input'\n  value = ''\n  placeholder?: string\n  type = 'text'\n  name?: string\n  disabled = false\n  accessibleLabel?: string\n\n  render() {\n    return html`<uip-input\n      part=\"base\"\n      data-sidebar=\"input\"\n      class=\"w-full [&::part(control)]:bg-background [&::part(control)]:h-8 [&::part(control)]:shadow-none\"\n      .value=${live(this.value)}\n      type=${this.type}\n      name=${this.name ?? nothing}\n      placeholder=${this.placeholder ?? nothing}\n      aria-label=${this.accessibleLabel ?? nothing}\n      ?disabled=${this.disabled}\n      @input=${(e: Event) => (this.value = (e.currentTarget as HTMLInputElement).value)}\n    ></uip-input>`\n  }\n}\n\n/** <uip-sidebar-separator> — React's SidebarSeparator (a <uip-separator>). */\nexport class UipSidebarSeparator extends SidebarElement {\n  protected slotName = 'sidebar-separator'\n\n  render() {\n    return html`<uip-separator\n      part=\"base\"\n      data-sidebar=\"separator\"\n      class=\"[&::part(base)]:bg-sidebar-border [&::part(base)]:mx-2 [&::part(base)]:w-auto\"\n    ></uip-separator>`\n  }\n}\n\n/* ------------------------------------------------------------------ */\n/* Layout parts                                                        */\n/* ------------------------------------------------------------------ */\n\n/** <uip-sidebar-header> — React's SidebarHeader. */\nexport class UipSidebarHeader extends SidebarElement {\n  protected slotName = 'sidebar-header'\n  render() {\n    return html`<div part=\"base\" data-uipkge=\"\" data-slot=\"sidebar-header\" data-sidebar=\"header\" class=\"flex flex-col gap-2 p-2\">\n      <slot></slot>\n    </div>`\n  }\n}\n\n/** <uip-sidebar-footer> — React's SidebarFooter. */\nexport class UipSidebarFooter extends SidebarElement {\n  protected slotName = 'sidebar-footer'\n  render() {\n    return html`<div part=\"base\" data-uipkge=\"\" data-slot=\"sidebar-footer\" data-sidebar=\"footer\" class=\"flex flex-col gap-2 p-2\">\n      <slot></slot>\n    </div>`\n  }\n}\n\n/** <uip-sidebar-content> — React's SidebarContent (the scrolling middle). */\nexport class UipSidebarContent extends SidebarElement {\n  protected slotName = 'sidebar-content'\n  render() {\n    return this.group(\n      html`<div\n        part=\"base\"\n        data-uipkge=\"\"\n        data-slot=\"sidebar-content\"\n        data-sidebar=\"content\"\n        class=\"flex min-h-0 flex-1 flex-col gap-2 overflow-auto group-data-[collapsible=icon]:overflow-hidden\"\n      >\n        <slot></slot>\n      </div>`,\n    )\n  }\n}\n\n/** <uip-sidebar-group> — React's SidebarGroup. */\nexport class UipSidebarGroup extends SidebarElement {\n  protected slotName = 'sidebar-group'\n  render() {\n    return html`<div\n      part=\"base\"\n      data-uipkge=\"\"\n      data-slot=\"sidebar-group\"\n      data-sidebar=\"group\"\n      class=\"relative flex w-full min-w-0 flex-col p-2\"\n    >\n      <slot></slot>\n    </div>`\n  }\n}\n\n/** <uip-sidebar-group-label> — React's SidebarGroupLabel (fades out in icon mode). */\nexport class UipSidebarGroupLabel extends SidebarElement {\n  protected slotName = 'sidebar-group-label'\n  render() {\n    return this.group(\n      html`<div\n        part=\"base\"\n        data-uipkge=\"\"\n        data-slot=\"sidebar-group-label\"\n        data-sidebar=\"group-label\"\n        class=${cn(\n          'text-sidebar-foreground/70 ring-sidebar-ring mt-2 mb-1 flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium outline-hidden transition-[margin,opacity] duration-200 ease-linear focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',\n          'group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0',\n        )}\n      >\n        <slot class=${slottedIcon}></slot>\n      </div>`,\n    )\n  }\n}\n\n/** <uip-sidebar-group-action> — React's SidebarGroupAction (icon button in the group's corner). */\nexport class UipSidebarGroupAction extends SidebarElement {\n  static properties = { accessibleLabel: { attribute: 'aria-label' } }\n  protected slotName = 'sidebar-group-action'\n  accessibleLabel?: string\n  render() {\n    return this.group(\n      html`<button\n        part=\"base\"\n        type=\"button\"\n        data-uipkge=\"\"\n        data-slot=\"sidebar-group-action\"\n        data-sidebar=\"group-action\"\n        aria-label=${this.accessibleLabel ?? nothing}\n        class=${cn(\n          'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground absolute top-3.5 right-3 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',\n          'after:absolute after:-inset-2 md:after:hidden',\n          'group-data-[collapsible=icon]:hidden',\n        )}\n      >\n        <slot class=${slottedIcon}></slot>\n      </button>`,\n    )\n  }\n}\n\n/** <uip-sidebar-group-content> — React's SidebarGroupContent. */\nexport class UipSidebarGroupContent extends SidebarElement {\n  protected slotName = 'sidebar-group-content'\n  render() {\n    return html`<div part=\"base\" data-uipkge=\"\" data-slot=\"sidebar-group-content\" data-sidebar=\"group-content\" class=\"w-full text-sm\">\n      <slot></slot>\n    </div>`\n  }\n}\n\n/* ------------------------------------------------------------------ */\n/* SidebarMenu family                                                  */\n/* ------------------------------------------------------------------ */\n\n/** <uip-sidebar-menu> — React's SidebarMenu (<ul>). Children: <uip-sidebar-menu-item>. */\nexport class UipSidebarMenu extends SidebarElement {\n  protected slotName = 'sidebar-menu'\n  render() {\n    return html`<ul part=\"base\" data-uipkge=\"\" data-slot=\"sidebar-menu\" data-sidebar=\"menu\" class=\"flex w-full min-w-0 flex-col gap-1\">\n      <slot></slot>\n    </ul>`\n  }\n}\n\n/**\n * <uip-sidebar-menu-item> — React's SidebarMenuItem (<li class=\"group/menu-item\">).\n * Its direct children (menu button, action, badge, sub menu) can't see the\n * <li>'s `:hover` / `:focus-within` / `:has()` across shadow roots, so the\n * item tracks hover and focus-within and re-renders them.\n */\nexport class UipSidebarMenuItem extends SidebarElement {\n  protected slotName = 'sidebar-menu-item'\n  hover = false\n  focusWithin = false\n\n  get menuButton(): UipSidebarMenuButton | null {\n    return this.querySelector(':scope > uip-sidebar-menu-button')\n  }\n\n  get hasAction() {\n    return !!this.querySelector(':scope > uip-sidebar-menu-action')\n  }\n\n  /** Re-render the item's direct sidebar children. */\n  refresh(source?: Element) {\n    for (const c of this.children) if (c !== source && c instanceof SidebarElement) c.requestUpdate()\n  }\n\n  private setHover(v: boolean) {\n    if (this.hover === v) return\n    this.hover = v\n    this.refresh()\n  }\n\n  private setFocusWithin(v: boolean) {\n    if (this.focusWithin === v) return\n    this.focusWithin = v\n    this.refresh()\n  }\n\n  render() {\n    return html`<li\n      part=\"base\"\n      data-uipkge=\"\"\n      data-slot=\"sidebar-menu-item\"\n      data-sidebar=\"menu-item\"\n      class=\"group/menu-item relative\"\n      @pointerenter=${(e: PointerEvent) => e.pointerType !== 'touch' && this.setHover(true)}\n      @pointerleave=${() => this.setHover(false)}\n      @focusin=${() => this.setFocusWithin(true)}\n      @focusout=${(e: FocusEvent) => {\n        const next = e.relatedTarget as Node | null\n        if (!next || !this.contains(next)) this.setFocusWithin(false)\n      }}\n    >\n      <slot @slotchange=${() => this.refresh()}></slot>\n    </li>`\n  }\n}\n\ntype MenuButtonVariant = NonNullable<SidebarMenuButtonVariants['variant']>\ntype MenuButtonSize = NonNullable<SidebarMenuButtonVariants['size']>\n\n/**\n * <uip-sidebar-menu-button> — React's SidebarMenuButton.\n *\n * Props: `is-active`, `variant` ('default' | 'outline'), `size` ('default' |\n * 'sm' | 'lg'), `tooltip` (text; shown on the right only while the sidebar is\n * collapsed and not mobile, like React), `disabled`, `aria-label`, and\n * `href` / `target` (renders an <a> — the stand-in for React's `asChild`).\n */\nexport class UipSidebarMenuButton extends SidebarElement {\n  static shadowRootOptions = { ...LitElement.shadowRootOptions, delegatesFocus: true }\n  static properties = {\n    isActive: { type: Boolean, attribute: 'is-active', reflect: true },\n    variant: { reflect: true },\n    size: { reflect: true },\n    tooltip: {},\n    disabled: { type: Boolean, reflect: true },\n    href: {},\n    target: {},\n    accessibleLabel: { attribute: 'aria-label' },\n  }\n\n  protected slotName = 'sidebar-menu-button'\n  isActive = false\n  variant: MenuButtonVariant = 'default'\n  size: MenuButtonSize = 'default'\n  tooltip?: string\n  disabled = false\n  href?: string\n  target?: string\n  accessibleLabel?: string\n  /** Pointer is over the button (mirrored to the action/badge as `peer-hover`). */\n  hovered = false\n\n  private setHovered(v: boolean) {\n    if (this.hovered === v) return\n    this.hovered = v\n    this.item?.refresh()\n  }\n\n  protected updated(changed: Map<string, unknown>) {\n    // The sibling action / badge mirror size + active.\n    if (changed.has('size') || changed.has('isActive')) this.item?.refresh(this)\n  }\n\n  render() {\n    const item = this.item\n    const classes = cn(sidebarMenuButtonVariants({ variant: this.variant, size: this.size }))\n    const slot = html`<slot\n      class=\"${slottedIcon} [&::slotted(svg)]:text-sidebar-foreground/70 [&::slotted(span:last-child)]:truncate\"\n    ></slot>`\n    const enter = (e: PointerEvent) => e.pointerType !== 'touch' && this.setHovered(true)\n    const leave = () => this.setHovered(false)\n    const button = this.href\n      ? html`<a\n          part=\"base\"\n          data-uipkge=\"\"\n          data-slot=\"sidebar-menu-button\"\n          data-sidebar=\"menu-button\"\n          data-size=${this.size}\n          data-active=${String(this.isActive)}\n          href=${this.disabled ? nothing : this.href}\n          target=${this.target ?? nothing}\n          aria-disabled=${this.disabled ? 'true' : nothing}\n          aria-label=${this.accessibleLabel ?? nothing}\n          class=${classes}\n          @pointerenter=${enter}\n          @pointerleave=${leave}\n          >${slot}</a\n        >`\n      : html`<button\n          part=\"base\"\n          type=\"button\"\n          data-uipkge=\"\"\n          data-slot=\"sidebar-menu-button\"\n          data-sidebar=\"menu-button\"\n          data-size=${this.size}\n          data-active=${String(this.isActive)}\n          ?disabled=${this.disabled}\n          aria-label=${this.accessibleLabel ?? nothing}\n          class=${classes}\n          @pointerenter=${enter}\n          @pointerleave=${leave}\n        >\n          ${slot}\n        </button>`\n    const hidden = this.sidebarState !== 'collapsed' || !!this.provider?.isMobile\n    const content = this.tooltip\n      ? html`<uip-tooltip\n          class=\"contents\"\n          content=${this.tooltip}\n          side=\"right\"\n          align=\"center\"\n          delay-duration=\"0\"\n          ?disabled=${hidden}\n          >${button}</uip-tooltip\n        >`\n      : button\n    // `group-has-data-[sidebar=menu-action]/menu-item:pr-8` needs an action\n    // inside the item: mirror one.\n    return this.group(\n      [item?.hasAction ? html`<i hidden data-sidebar=\"menu-action\"></i>` : html``, content],\n      'group/menu-item',\n      item,\n    )\n  }\n}\n\n/**\n * <uip-sidebar-menu-action> — React's SidebarMenuAction (icon button at the\n * row's right edge). `show-on-hover` reveals it only on row hover / focus\n * (≥ md), like React. `aria-label` is forwarded.\n */\nexport class UipSidebarMenuAction extends SidebarElement {\n  static properties = {\n    showOnHover: { type: Boolean, attribute: 'show-on-hover' },\n    accessibleLabel: { attribute: 'aria-label' },\n  }\n  protected slotName = 'sidebar-menu-action'\n  showOnHover = false\n  accessibleLabel?: string\n\n  render() {\n    return this.group(\n      [\n        this.menuButtonPeer(),\n        html`<button\n          part=\"base\"\n          type=\"button\"\n          data-uipkge=\"\"\n          data-slot=\"sidebar-menu-action\"\n          data-sidebar=\"menu-action\"\n          aria-label=${this.accessibleLabel ?? nothing}\n          class=${cn(\n            'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground peer-hover/menu-button:text-sidebar-accent-foreground absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded-md p-0 outline-hidden transition-transform focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0',\n            'after:absolute after:-inset-2 md:after:hidden',\n            'peer-data-[size=sm]/menu-button:top-1',\n            'peer-data-[size=default]/menu-button:top-1.5',\n            'peer-data-[size=lg]/menu-button:top-2.5',\n            'group-data-[collapsible=icon]:hidden',\n            this.showOnHover &&\n              'peer-data-[active=true]/menu-button:text-sidebar-accent-foreground group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 data-[state=open]:opacity-100 md:opacity-0',\n            // Shadow adaptation: :hover / :focus-within of the item and the\n            // sibling button can't be matched from here; the mirrors carry\n            // data-hover / data-focus-within instead.\n            'peer-data-[hover]/menu-button:text-sidebar-accent-foreground',\n            this.showOnHover && 'group-data-[focus-within]/menu-item:opacity-100 group-data-[hover]/menu-item:opacity-100',\n          )}\n        >\n          <slot class=${slottedIcon}></slot>\n        </button>`,\n      ],\n      'group/menu-item',\n      this.item,\n    )\n  }\n}\n\n/** <uip-sidebar-menu-badge> — React's SidebarMenuBadge (count at the row's right edge). */\nexport class UipSidebarMenuBadge extends SidebarElement {\n  protected slotName = 'sidebar-menu-badge'\n  render() {\n    return this.group([\n      this.menuButtonPeer(),\n      html`<div\n        part=\"base\"\n        data-uipkge=\"\"\n        data-slot=\"sidebar-menu-badge\"\n        data-sidebar=\"menu-badge\"\n        class=${cn(\n          'text-sidebar-foreground pointer-events-none absolute right-1 flex h-5 min-w-5 items-center justify-center rounded-md px-1 text-xs font-medium tabular-nums select-none',\n          'peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[active=true]/menu-button:text-sidebar-accent-foreground',\n          'peer-data-[size=sm]/menu-button:top-1',\n          'peer-data-[size=default]/menu-button:top-1.5',\n          'peer-data-[size=lg]/menu-button:top-2.5',\n          'group-data-[collapsible=icon]:hidden',\n          // Shadow adaptation (see menu-action).\n          'peer-data-[hover]/menu-button:text-sidebar-accent-foreground',\n        )}\n      >\n        <slot></slot>\n      </div>`,\n    ])\n  }\n}\n\n/**\n * <uip-sidebar-menu-skeleton> — React's SidebarMenuSkeleton: optional icon\n * block (`show-icon`) and a text bar `width` wide (default 70%).\n */\nexport class UipSidebarMenuSkeleton extends SidebarElement {\n  static properties = {\n    showIcon: { type: Boolean, attribute: 'show-icon' },\n    width: {},\n  }\n  protected slotName = 'sidebar-menu-skeleton'\n  showIcon = false\n  width = '70%'\n\n  render() {\n    return html`<div\n      part=\"base\"\n      data-uipkge=\"\"\n      data-slot=\"sidebar-menu-skeleton\"\n      data-sidebar=\"menu-skeleton\"\n      class=\"flex h-8 items-center gap-2 rounded-md px-2\"\n    >\n      ${this.showIcon ? html`<uip-skeleton class=\"size-4 rounded-md\" data-sidebar=\"menu-skeleton-icon\"></uip-skeleton>` : nothing}\n      <uip-skeleton\n        class=\"h-4 max-w-(--skeleton-width) flex-1\"\n        data-sidebar=\"menu-skeleton-text\"\n        style=${styleMap({ '--skeleton-width': this.width })}\n      ></uip-skeleton>\n    </div>`\n  }\n}\n\n/**\n * <uip-sidebar-menu-sub> — React's SidebarMenuSub (indented child <ul>).\n * React tags it `data-sidebar=\"menu-badge\"`; kept verbatim.\n */\nexport class UipSidebarMenuSub extends SidebarElement {\n  protected slotName = 'sidebar-menu-sub'\n  render() {\n    return this.group(\n      html`<ul\n        part=\"base\"\n        data-uipkge=\"\"\n        data-slot=\"sidebar-menu-sub\"\n        data-sidebar=\"menu-badge\"\n        class=${cn(\n          'border-sidebar-border mx-3.5 flex min-w-0 translate-x-px flex-col gap-1 border-l px-2.5 py-0.5',\n          'group-data-[collapsible=icon]:hidden',\n        )}\n      >\n        <slot></slot>\n      </ul>`,\n    )\n  }\n}\n\n/** <uip-sidebar-menu-sub-item> — React's SidebarMenuSubItem (<li>). */\nexport class UipSidebarMenuSubItem extends SidebarElement {\n  protected slotName = 'sidebar-menu-sub-item'\n  render() {\n    return html`<li\n      part=\"base\"\n      data-uipkge=\"\"\n      data-slot=\"sidebar-menu-sub-item\"\n      data-sidebar=\"menu-sub-item\"\n      class=\"group/menu-sub-item relative\"\n    >\n      <slot></slot>\n    </li>`\n  }\n}\n\n/**\n * <uip-sidebar-menu-sub-button> — React's SidebarMenuSubButton (an <a>).\n * Props: `href`, `target`, `size` ('sm' | 'md'), `is-active`, `aria-label`.\n */\nexport class UipSidebarMenuSubButton extends SidebarElement {\n  static shadowRootOptions = { ...LitElement.shadowRootOptions, delegatesFocus: true }\n  static properties = {\n    href: {},\n    target: {},\n    size: { reflect: true },\n    isActive: { type: Boolean, attribute: 'is-active', reflect: true },\n    accessibleLabel: { attribute: 'aria-label' },\n  }\n  protected slotName = 'sidebar-menu-sub-button'\n  href?: string\n  target?: string\n  size: 'sm' | 'md' = 'md'\n  isActive = false\n  accessibleLabel?: string\n\n  render() {\n    const size = this.size\n    return this.group(\n      html`<a\n        part=\"base\"\n        data-uipkge=\"\"\n        data-slot=\"sidebar-menu-sub-button\"\n        data-sidebar=\"menu-sub-button\"\n        data-size=${size}\n        data-active=${String(this.isActive)}\n        href=${this.href ?? nothing}\n        target=${this.target ?? nothing}\n        aria-label=${this.accessibleLabel ?? nothing}\n        class=${cn(\n          'text-sidebar-foreground ring-sidebar-ring hover:bg-sidebar-accent hover:text-sidebar-accent-foreground active:bg-sidebar-accent active:text-sidebar-accent-foreground [&>svg]:text-sidebar-accent-foreground flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 outline-hidden focus-visible:ring-2 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0',\n          'data-[active=true]:bg-sidebar-accent data-[active=true]:text-sidebar-accent-foreground',\n          size === 'sm' && 'text-xs',\n          size === 'md' && 'text-sm',\n          'group-data-[collapsible=icon]:hidden',\n        )}\n        ><slot\n          class=\"${slottedIcon} [&::slotted(svg)]:text-sidebar-accent-foreground [&::slotted(span:last-child)]:truncate\"\n        ></slot\n      ></a>`,\n    )\n  }\n}\n\n/* ------------------------------------------------------------------ */\n/* Registration (provider first, so parts find an upgraded provider)   */\n/* ------------------------------------------------------------------ */\n\nconst define = (tag: string, cls: CustomElementConstructor) => customElements.get(tag) || customElements.define(tag, cls)\ndefine('uip-sidebar-provider', UipSidebarProvider)\ndefine('uip-sidebar', UipSidebar)\ndefine('uip-sidebar-trigger', UipSidebarTrigger)\ndefine('uip-sidebar-rail', UipSidebarRail)\ndefine('uip-sidebar-inset', UipSidebarInset)\ndefine('uip-sidebar-input', UipSidebarInput)\ndefine('uip-sidebar-separator', UipSidebarSeparator)\ndefine('uip-sidebar-header', UipSidebarHeader)\ndefine('uip-sidebar-footer', UipSidebarFooter)\ndefine('uip-sidebar-content', UipSidebarContent)\ndefine('uip-sidebar-group', UipSidebarGroup)\ndefine('uip-sidebar-group-label', UipSidebarGroupLabel)\ndefine('uip-sidebar-group-action', UipSidebarGroupAction)\ndefine('uip-sidebar-group-content', UipSidebarGroupContent)\ndefine('uip-sidebar-menu', UipSidebarMenu)\ndefine('uip-sidebar-menu-item', UipSidebarMenuItem)\ndefine('uip-sidebar-menu-button', UipSidebarMenuButton)\ndefine('uip-sidebar-menu-action', UipSidebarMenuAction)\ndefine('uip-sidebar-menu-badge', UipSidebarMenuBadge)\ndefine('uip-sidebar-menu-skeleton', UipSidebarMenuSkeleton)\ndefine('uip-sidebar-menu-sub', UipSidebarMenuSub)\ndefine('uip-sidebar-menu-sub-item', UipSidebarMenuSubItem)\ndefine('uip-sidebar-menu-sub-button', UipSidebarMenuSubButton)\n\ndeclare global {\n  interface HTMLElementTagNameMap {\n    'uip-sidebar-provider': UipSidebarProvider\n    'uip-sidebar': UipSidebar\n    'uip-sidebar-trigger': UipSidebarTrigger\n    'uip-sidebar-rail': UipSidebarRail\n    'uip-sidebar-inset': UipSidebarInset\n    'uip-sidebar-input': UipSidebarInput\n    'uip-sidebar-separator': UipSidebarSeparator\n    'uip-sidebar-header': UipSidebarHeader\n    'uip-sidebar-footer': UipSidebarFooter\n    'uip-sidebar-content': UipSidebarContent\n    'uip-sidebar-group': UipSidebarGroup\n    'uip-sidebar-group-label': UipSidebarGroupLabel\n    'uip-sidebar-group-action': UipSidebarGroupAction\n    'uip-sidebar-group-content': UipSidebarGroupContent\n    'uip-sidebar-menu': UipSidebarMenu\n    'uip-sidebar-menu-item': UipSidebarMenuItem\n    'uip-sidebar-menu-button': UipSidebarMenuButton\n    'uip-sidebar-menu-action': UipSidebarMenuAction\n    'uip-sidebar-menu-badge': UipSidebarMenuBadge\n    'uip-sidebar-menu-skeleton': UipSidebarMenuSkeleton\n    'uip-sidebar-menu-sub': UipSidebarMenuSub\n    'uip-sidebar-menu-sub-item': UipSidebarMenuSubItem\n    'uip-sidebar-menu-sub-button': UipSidebarMenuSubButton\n  }\n}\n",
      "type": "registry:file",
      "target": "src/uip/components/sidebar/sidebar.ts"
    },
    {
      "path": "packages/elements-lit/src/components/sidebar/sidebar.variants.ts",
      "content": "import type { VariantProps } from 'class-variance-authority'\nimport { cva } from 'class-variance-authority'\n\n/**\n * Variant definitions live in their own file (rather than the package\n * `index.ts`) so consuming Vue SFCs can import without creating a circular\n * dependency through the index. See card.variants.ts for the canonical\n * example + the SSR symptom that motivated the split.\n */\n\nexport const sidebarMenuButtonVariants = cva(\n  'peer/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm text-sidebar-foreground/80 outline-hidden ring-sidebar-ring transition-[width,height,padding,color,background-color] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 group-has-data-[sidebar=menu-action]/menu-item:pr-8 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[active=true]:bg-sidebar-accent data-[active=true]:font-medium data-[active=true]:text-sidebar-accent-foreground data-[state=open]:hover:bg-sidebar-accent data-[state=open]:hover:text-sidebar-accent-foreground group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-foreground/70',\n  {\n    variants: {\n      variant: {\n        default: 'hover:bg-sidebar-accent hover:text-foreground',\n        outline:\n          'bg-background shadow-[0_0_0_1px_var(--sidebar-border)] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_var(--sidebar-accent)]',\n      },\n      size: {\n        default: 'h-8 text-sm',\n        sm: 'h-7 text-xs',\n        lg: 'h-12 text-sm group-data-[collapsible=icon]:p-0!',\n      },\n    },\n    defaultVariants: {\n      variant: 'default',\n      size: 'default',\n    },\n  },\n)\n\nexport type SidebarMenuButtonVariants = VariantProps<typeof sidebarMenuButtonVariants>\n",
      "type": "registry:file",
      "target": "src/uip/components/sidebar/sidebar.variants.ts"
    }
  ],
  "dependencies": [
    "class-variance-authority",
    "lit",
    "lucide"
  ],
  "registryDependencies": [
    "https://uipkge.dev/r/lit/lit-core.json"
  ]
}
