///|
pub(all) enum SidebarSide {
  SidebarLeft
  SidebarRight
} derive(Debug, Eq)

///|
pub(all) enum SidebarVariant {
  SidebarDefault
  SidebarFloating
  SidebarInset
} derive(Debug, Eq)

///|
pub(all) enum SidebarCollapsible {
  SidebarOffcanvas
  SidebarIcon
  SidebarFixed
} derive(Debug, Eq)

///|
pub(all) enum SidebarMenuButtonSize {
  SidebarMenuSm
  SidebarMenuDefault
  SidebarMenuLg
} derive(Debug, Eq)

///|
pub(all) enum SidebarMenuButtonVariant {
  SidebarMenuButtonDefault
  SidebarMenuButtonOutline
} derive(Debug, Eq)

///|
#cfg(target="js")
priv struct SidebarState {
  collapsed : Bool
  mobile_open : Bool
  desktop_width : Int?
} derive(Eq)

///|
#cfg(target="js")
priv enum SidebarMessage {
  ToggleSidebarCollapsed
  SetSidebarCollapsed(Bool)
  ToggleSidebarMobile
  SetSidebarMobile(Bool)
  CommitSidebarWidth(Int)
}

///|
/// Explicit state and commands shared by every part inside
/// [`sidebar_provider`].
struct SidebarScope {
  collapsed : Bool
  mobile_open : Bool
  desktop_width : Int?
  toggle_collapsed : @cmd.Cmd
  expand : @cmd.Cmd
  collapse : @cmd.Cmd
  toggle_mobile : @cmd.Cmd
  open_mobile : @cmd.Cmd
  close_mobile : @cmd.Cmd
  commit_width : @cmd.Emit[Int]
}

///|
const SidebarProviderStyle : String = "position:relative;display:flex;width:100%;min-height:100svh;align-items:stretch;background:var(--rui-sidebar-wrapper-background,var(--rui-background,oklch(1 0 0)));color:var(--rui-foreground,oklch(0.145 0 0))"

///|
const SidebarShellStyle : String = "position:relative;min-width:0;align-self:stretch;flex:none;transition:width 200ms linear"

///|
const SidebarBaseStyle : String = "position:relative;display:flex;min-width:0;flex-direction:column;flex-shrink:0;overflow:visible;background:var(--rui-sidebar,oklch(0.985 0 0));color:var(--rui-sidebar-foreground,var(--rui-foreground,oklch(0.145 0 0)));transition:width 200ms linear,transform 200ms linear"

///|
const SidebarDesktopStyle : String = "position:absolute;top:0;bottom:0;z-index:10"

///|
const SidebarMobileLayerStyle : String = "position:fixed;inset:0;width:auto;height:auto;max-width:none;max-height:none;margin:0;border:0;padding:0;background:transparent;color:inherit;overflow:visible;pointer-events:none;transition:display 200ms,overlay 200ms;transition-behavior:allow-discrete"

///|
const SidebarMobileStyle : String = "position:fixed;top:0;bottom:0;z-index:1;width:min(var(--rui-sidebar-width-mobile,18rem),calc(100vw - 2rem));max-width:100vw;margin:0;border-block:0;padding:0;overflow:hidden;pointer-events:auto;outline:none;box-shadow:0 16px 48px rgb(0 0 0 / 0.22);transition:transform 200ms ease-in-out,opacity 200ms ease-in-out"

///|
const SidebarOverlayStyle : String = "position:fixed;inset:0;z-index:0;background:rgb(0 0 0 / 0.5);opacity:var(--rui-sidebar-overlay-opacity,0);visibility:var(--rui-sidebar-overlay-visibility,hidden);pointer-events:var(--rui-sidebar-overlay-pointer-events,none);transition:opacity 200ms ease,visibility 0s linear var(--rui-sidebar-overlay-visibility-delay,200ms)"

///|
const SidebarOverlayOpenStyle : String = "--rui-sidebar-overlay-opacity:1;--rui-sidebar-overlay-visibility:visible;--rui-sidebar-overlay-pointer-events:auto;--rui-sidebar-overlay-visibility-delay:0s"

///|
const SidebarOverlayClosedStyle : String = "--rui-sidebar-overlay-opacity:0;--rui-sidebar-overlay-visibility:hidden;--rui-sidebar-overlay-pointer-events:auto;--rui-sidebar-overlay-visibility-delay:200ms"

///|
const SidebarHeaderFooterStyle : String = "display:flex;min-width:0;flex-direction:column;gap:0.5rem;padding:0.5rem;overflow:hidden"

///|
const SidebarContentStyle : String = "display:flex;min-width:0;min-height:0;flex:1;flex-direction:column;gap:0.5rem;overflow-x:hidden;overflow-y:var(--rui-sidebar-content-overflow-y,auto);scrollbar-width:none"

///|
const SidebarGroupStyle : String = "position:relative;display:flex;min-width:0;flex-direction:column;padding:0.5rem"

///|
const SidebarGroupLabelStyle : String = "display:flex;height:2rem;min-width:0;align-items:center;margin-block-start:var(--rui-sidebar-group-label-local-margin,var(--rui-sidebar-group-label-margin,0));padding-inline:0.5rem;color:var(--rui-sidebar-label-fg,var(--rui-sidebar-label-base-fg,color-mix(in oklab,var(--rui-sidebar-foreground,var(--rui-foreground,oklch(0.145 0 0))) 70%,transparent)));font-size:0.75rem;line-height:1rem;font-weight:500;white-space:nowrap;opacity:var(--rui-sidebar-group-label-local-opacity,var(--rui-sidebar-group-label-opacity,1));overflow:hidden;pointer-events:var(--rui-sidebar-group-label-local-pointer-events,var(--rui-sidebar-group-label-pointer-events,auto));transition:margin 200ms linear,opacity 150ms ease"

///|
const SidebarGroupActionStyle : String = "position:absolute;top:0.875rem;inset-inline-end:0.75rem;z-index:2;display:var(--rui-sidebar-group-action-display,inline-flex)"

///|
const SidebarActionSurfaceStyle : String = "border-color:var(--rui-sidebar-action-border,var(--rui-button-border,var(--rui-button-base-border,transparent)));background:var(--rui-sidebar-action-bg,var(--rui-button-bg,var(--rui-button-base-bg,transparent)));color:var(--rui-sidebar-action-fg,var(--rui-sidebar-foreground,inherit));box-shadow:var(--rui-sidebar-action-shadow,var(--rui-button-shadow,var(--rui-button-base-shadow,none)))"

///|
const SidebarActionSizeStyle : String = "width:1.25rem;height:1.25rem;--rui-button-icon-size:1rem;--rui-button-padding-inline:0"

///|
const SidebarGroupContentStyle : String = "min-width:0;width:100%;font-size:0.875rem;line-height:1.25rem"

///|
const SidebarMenuStyle : String = "display:flex;min-width:0;width:100%;flex-direction:column;gap:0.25rem;margin:0;padding:0;list-style:none"

///|
const SidebarMenuItemStyle : String = "position:relative;display:block;min-width:0"

///|
const SidebarMenuButtonStyle : String = "display:flex;width:var(--rui-sidebar-item-collapse-width,100%);min-width:0;align-items:center;justify-content:var(--rui-sidebar-item-justify-content,flex-start);gap:var(--rui-sidebar-item-collapse-gap,0.5rem);border:1px solid transparent;border-color:var(--rui-sidebar-item-border,var(--rui-sidebar-item-base-border,transparent));border-radius:calc(var(--rui-radius,0.625rem) - 0.125rem);margin:0;padding-inline-start:0.5rem;padding-inline-end:var(--rui-sidebar-item-local-padding-end,var(--rui-sidebar-item-padding-end,0.5rem));background:var(--rui-sidebar-item-bg,var(--rui-sidebar-item-base-bg,transparent));color:var(--rui-sidebar-item-fg,var(--rui-sidebar-item-base-fg,inherit));box-shadow:var(--rui-sidebar-item-shadow,var(--rui-sidebar-item-base-shadow,none));font-size:var(--rui-sidebar-item-collapse-font-size,var(--rui-sidebar-item-base-font-size,0.875rem));line-height:1.25rem;font-weight:var(--rui-sidebar-item-font-weight,var(--rui-sidebar-item-base-font-weight,400));text-align:start;text-decoration:none;white-space:nowrap;cursor:pointer;outline-offset:2px;overflow:hidden;appearance:none;-webkit-appearance:none;transition:width 200ms linear,height 200ms linear,gap 200ms linear,padding 200ms linear,font-size 150ms ease,color 150ms ease,background-color 150ms ease,border-color 150ms ease,box-shadow 150ms ease"

///|
const SidebarMenuButtonActiveStyle : String = "--rui-sidebar-item-base-bg:var(--rui-sidebar-accent,var(--rui-accent,oklch(0.97 0 0)));--rui-sidebar-item-base-fg:var(--rui-sidebar-accent-foreground,var(--rui-accent-foreground,var(--rui-foreground,oklch(0.145 0 0))));--rui-sidebar-item-base-font-weight:500"

///|
const SidebarMenuButtonOutlineStyle : String = "--rui-sidebar-item-base-bg:var(--rui-background,oklch(1 0 0));--rui-sidebar-item-base-shadow:0 0 0 1px var(--rui-sidebar-border,var(--rui-border,oklch(0.922 0 0)))"

///|
const SidebarMenuActionStyle : String = "position:absolute;top:var(--rui-sidebar-menu-action-top,0.375rem);inset-inline-end:0.25rem;z-index:2;display:var(--rui-sidebar-menu-action-display,inline-flex);opacity:var(--rui-sidebar-action-opacity,var(--rui-sidebar-action-base-opacity,1));transform:translateY(var(--rui-sidebar-action-press-offset,0))"

///|
const SidebarMenuActionHoverStyle : String = "--rui-sidebar-action-base-opacity:0"

///|
const SidebarRailStyle : String = "position:absolute;top:0;bottom:0;left:var(--rui-sidebar-rail-left,auto);right:var(--rui-sidebar-rail-right,-0.5rem);z-index:4;width:1rem;height:auto;border:0;border-radius:0;margin:0;padding:0;background:transparent;color:inherit;box-shadow:none;cursor:var(--rui-sidebar-rail-cursor,col-resize);touch-action:none;user-select:none"

///|
const SidebarRailLineStyle : String = "position:absolute;top:0;bottom:0;left:var(--rui-sidebar-rail-line-left,50%);display:block;width:2px;background:var(--rui-sidebar-rail-line-bg,transparent);transition:background-color 150ms ease;pointer-events:none"

///|
const SidebarMenuBadgeStyle : String = "position:absolute;top:var(--rui-sidebar-menu-badge-top,0.375rem);inset-inline-end:var(--rui-sidebar-badge-local-end,var(--rui-sidebar-badge-end,0.5rem));display:var(--rui-sidebar-menu-badge-display,flex);min-width:1.25rem;height:1.25rem;align-items:center;justify-content:center;border-radius:0.25rem;padding-inline:0.25rem;color:var(--rui-sidebar-badge-fg,var(--rui-sidebar-badge-base-fg,inherit));font-size:0.75rem;line-height:1rem;font-variant-numeric:tabular-nums;pointer-events:none"

///|
const SidebarMenuSubStyle : String = "display:var(--rui-sidebar-menu-sub-display,flex);min-width:0;flex-direction:column;gap:0.125rem;margin-block:0.25rem;margin-inline:0.875rem;padding-block:0.25rem;padding-inline:0.625rem;border-inline-start:1px solid var(--rui-sidebar-border,var(--rui-border,oklch(0.922 0 0)));list-style:none"

///|
const SidebarMenuSubButtonStyle : String = "display:flex;width:100%;min-width:0;height:1.75rem;align-items:center;gap:0.5rem;border:0;border-radius:calc(var(--rui-radius,0.625rem) - 0.25rem);padding-inline:0.5rem;background:var(--rui-sidebar-item-bg,var(--rui-sidebar-item-base-bg,transparent));color:var(--rui-sidebar-item-fg,var(--rui-sidebar-item-base-fg,inherit));box-shadow:var(--rui-sidebar-item-shadow,var(--rui-sidebar-item-base-shadow,none));font-size:0.8125rem;line-height:1rem;font-weight:var(--rui-sidebar-item-font-weight,var(--rui-sidebar-item-base-font-weight,400));text-align:start;text-decoration:none;white-space:nowrap;overflow:hidden;cursor:pointer;outline-offset:2px"

///|
const SidebarInsetStyle : String = "position:relative;display:flex;min-width:0;flex:1;flex-direction:column;background:var(--rui-background,oklch(1 0 0));transition:margin 200ms linear,border-radius 200ms linear,box-shadow 200ms linear"

///|
fn sidebar_side_value(side : SidebarSide) -> String {
  match side {
    SidebarLeft => "left"
    SidebarRight => "right"
  }
}

///|
fn sidebar_variant_value(variant : SidebarVariant) -> String {
  match variant {
    SidebarDefault => "sidebar"
    SidebarFloating => "floating"
    SidebarInset => "inset"
  }
}

///|
fn sidebar_collapsible_value(collapsible : SidebarCollapsible) -> String {
  match collapsible {
    SidebarOffcanvas => "offcanvas"
    SidebarIcon => "icon"
    SidebarFixed => "none"
  }
}

///|
fn sidebar_state_value(collapsed : Bool) -> String {
  if collapsed {
    "collapsed"
  } else {
    "expanded"
  }
}

///|
#cfg(target="js")
priv struct SidebarPointerState {
  rail : @dom.Element
  root : @dom.Element
  sidebar : @dom.Element
  shell : @dom.Element
  pointer_id : Int
  right : Bool
  gutter : Int
  start_x : Int
  start_width : Double
  min : Double
  max : Double
  mut width : Double
  mut active : Bool
  was_collapsed : Bool
  original_current_width : String
  original_sidebar_width : String
  original_transition : String
  original_shell_width : String
  original_shell_transition : String
  original_user_select : String
  original_cursor : String
  mut captured : Bool
  mut suppress_click : @dom.Listener?
  mut lost_pointer_capture : @dom.Listener?
}

///|
#cfg(target="js")
let sidebar_pointer_states : Array[SidebarPointerState] = []

///|
#cfg(target="js")
fn sidebar_pointer_state(rail : @dom.Element) -> SidebarPointerState? {
  for state in sidebar_pointer_states {
    if state.rail.is_same_node(rail.as_node()) {
      return Some(state)
    }
  }
  None
}

///|
#cfg(target="js")
fn sidebar_remove_pointer_state(rail : @dom.Element) -> Unit {
  for index, state in sidebar_pointer_states {
    if state.rail.is_same_node(rail.as_node()) {
      ignore(sidebar_pointer_states.remove(index))
      return
    }
  }
}

///|
#cfg(target="js")
fn sidebar_restore_style_property(
  style : @dom.CSSStyleDeclaration,
  name : String,
  value : String,
) -> Unit {
  if value == "" {
    ignore(style.remove_property(name))
  } else {
    style.set_property(name, value)
  }
}

///|
#cfg(target="js")
fn sidebar_remove_click_suppression(state : SidebarPointerState) -> Unit {
  if state.suppress_click is Some(listener) {
    state.rail.remove_event_listener_with_options(
      "click",
      listener,
      capture=true,
    )
    state.suppress_click = None
  }
}

///|
#cfg(target="js")
fn sidebar_install_click_suppression(state : SidebarPointerState) -> Unit {
  if state.suppress_click is Some(_) {
    return
  }
  let listener : @dom.Listener = event => {
    event.prevent_default()
    event.stop_immediate_propagation()
    event.stop_propagation()
    sidebar_remove_click_suppression(state)
  }
  state.suppress_click = Some(listener)
  state.rail.add_event_listener_with_options("click", listener, capture=true)
}

///|
#cfg(target="js")
fn sidebar_release_pointer_state(
  state : SidebarPointerState,
  committed : Bool,
) -> Unit {
  let dragged = state.active
  guard state.root.to_html_element() is Some(root) else {
    sidebar_remove_pointer_state(state.rail)
    return
  }
  guard state.sidebar.to_html_element() is Some(sidebar) else {
    sidebar_remove_pointer_state(state.rail)
    return
  }
  guard state.shell.to_html_element() is Some(shell) else {
    sidebar_remove_pointer_state(state.rail)
    return
  }
  let root_style = root.get_style()
  let sidebar_style = sidebar.get_style()
  let shell_style = shell.get_style()
  if committed && dragged {
    sidebar_restore_style_property(
      sidebar_style,
      "transition",
      state.original_transition,
    )
    sidebar_restore_style_property(
      shell_style,
      "transition",
      state.original_shell_transition,
    )
    sidebar_restore_style_property(
      root_style,
      "user-select",
      state.original_user_select,
    )
    sidebar_restore_style_property(root_style, "cursor", state.original_cursor)
    if !state.was_collapsed {
      sidebar_restore_style_property(
        sidebar_style,
        "width",
        state.original_sidebar_width,
      )
      sidebar_restore_style_property(
        shell_style,
        "width",
        state.original_shell_width,
      )
    }
  } else {
    sidebar_restore_style_property(
      root_style,
      "--rui-sidebar-current-width",
      state.original_current_width,
    )
    sidebar_restore_style_property(
      sidebar_style,
      "width",
      state.original_sidebar_width,
    )
    sidebar_restore_style_property(
      sidebar_style,
      "transition",
      state.original_transition,
    )
    sidebar_restore_style_property(
      shell_style,
      "width",
      state.original_shell_width,
    )
    sidebar_restore_style_property(
      shell_style,
      "transition",
      state.original_shell_transition,
    )
    sidebar_restore_style_property(
      root_style,
      "user-select",
      state.original_user_select,
    )
    sidebar_restore_style_property(root_style, "cursor", state.original_cursor)
  }
  state.root.remove_attribute("data-resizing")
  state.sidebar.remove_attribute("data-resizing")
  state.rail.remove_attribute("data-resizing")
  if state.lost_pointer_capture is Some(listener) {
    state.rail.remove_event_listener("lostpointercapture", listener)
    state.lost_pointer_capture = None
  }
  sidebar_remove_pointer_state(state.rail)
  if state.captured {
    state.captured = false
    if state.rail.has_pointer_capture(state.pointer_id) {
      state.rail.release_pointer_capture(state.pointer_id)
    }
  }
  if committed && dragged {
    ignore(
      @dom.window().set_timeout(
        () => sidebar_remove_click_suppression(state),
        0,
      ),
    )
  } else {
    sidebar_remove_click_suppression(state)
  }
}

///|
#cfg(target="js")
fn sidebar_pointer_start(
  event : @dom.MouseEvent,
  min_width : Int,
  max_width : Int,
) -> Bool {
  guard event.to_pointer_event() is Some(pointer) else { return false }
  if pointer.get_default_prevented() ||
    !pointer.get_is_primary() ||
    (pointer.get_pointer_type() == "mouse" && pointer.get_button() != 0) {
    return false
  }
  guard pointer.current_target().to_option() is Some(current_target) else {
    return false
  }
  guard current_target.to_element() is Some(rail) else { return false }
  guard rail.closest("[data-slot=\"sidebar-wrapper\"]") is Some(root) else {
    return false
  }
  guard rail.closest("[data-slot=\"sidebar\"]") is Some(sidebar) else {
    return false
  }
  guard sidebar.closest("[data-slot=\"sidebar-shell\"]") is Some(shell) else {
    return false
  }
  if sidebar.get_attribute("data-mobile").unwrap_or("") == "true" ||
    sidebar.get_attribute("data-state").unwrap_or("") == "collapsed" {
    return false
  }
  if sidebar_pointer_state(rail) is Some(previous) {
    sidebar_release_pointer_state(previous, false)
  }
  guard root.to_html_element() is Some(root_html) else { return false }
  guard sidebar.to_html_element() is Some(sidebar_html) else { return false }
  guard shell.to_html_element() is Some(shell_html) else { return false }
  let gutter = if shell.get_attribute("data-variant").unwrap_or("") == "sidebar" {
    0
  } else {
    16
  }
  let min = min_width.max(1).to_double()
  let available = root.get_bounding_client_rect().get_width() - 52.0
  if available < min {
    return false
  }
  let max = max_width.max(min_width.max(1)).to_double().min(available).max(min)
  let start_width = (sidebar.get_bounding_client_rect().get_width() +
  gutter.to_double()).clamp(min~, max~)
  let root_style = root_html.get_style()
  let sidebar_style = sidebar_html.get_style()
  let shell_style = shell_html.get_style()
  let state : SidebarPointerState = {
    rail,
    root,
    sidebar,
    shell,
    pointer_id: pointer.get_pointer_id(),
    right: sidebar.get_attribute("data-side").unwrap_or("") == "right",
    gutter,
    start_x: pointer.get_client_x(),
    start_width,
    min,
    max,
    width: start_width,
    active: false,
    was_collapsed: false,
    original_current_width: root_style.get_property_value(
      "--rui-sidebar-current-width",
    ),
    original_sidebar_width: sidebar_style.get_property_value("width"),
    original_transition: sidebar_style.get_property_value("transition"),
    original_shell_width: shell_style.get_property_value("width"),
    original_shell_transition: shell_style.get_property_value("transition"),
    original_user_select: root_style.get_property_value("user-select"),
    original_cursor: root_style.get_property_value("cursor"),
    captured: false,
    suppress_click: None,
    lost_pointer_capture: None,
  }
  let lost_pointer_capture : @dom.Listener = lost => {
    if lost.to_pointer_event() is Some(lost_pointer) &&
      lost_pointer.get_pointer_id() == state.pointer_id &&
      sidebar_pointer_state(state.rail) is Some(_) {
      sidebar_release_pointer_state(state, false)
    }
  }
  state.lost_pointer_capture = Some(lost_pointer_capture)
  rail.add_event_listener("lostpointercapture", lost_pointer_capture)
  sidebar_pointer_states.push(state)
  rail.set_pointer_capture(state.pointer_id)
  if rail.has_pointer_capture(state.pointer_id) {
    state.captured = true
    true
  } else {
    sidebar_release_pointer_state(state, false)
    false
  }
}

///|
#cfg(target="js")
fn sidebar_pointer_move(event : @dom.MouseEvent) -> Bool {
  guard event.to_pointer_event() is Some(pointer) else { return false }
  guard pointer.current_target().to_option() is Some(current_target) else {
    return false
  }
  guard current_target.to_element() is Some(rail) else { return false }
  guard sidebar_pointer_state(rail) is Some(state) else { return false }
  guard state.pointer_id == pointer.get_pointer_id() else { return false }
  guard state.root.to_html_element() is Some(root) else { return false }
  guard state.sidebar.to_html_element() is Some(sidebar) else { return false }
  guard state.shell.to_html_element() is Some(shell) else { return false }
  let physical_delta = pointer.get_client_x() - state.start_x
  if !state.active {
    if physical_delta.abs() < 4 {
      return false
    }
    state.active = true
    sidebar_install_click_suppression(state)
    state.root.set_attribute("data-resizing", "true")
    state.sidebar.set_attribute("data-resizing", "true")
    state.rail.set_attribute("data-resizing", "true")
    root.get_style().set_property("user-select", "none")
    root.get_style().set_property("cursor", "col-resize")
  }
  let delta = if state.right { -physical_delta } else { physical_delta }
  state.width = (state.start_width + delta.to_double()).clamp(
    min=state.min,
    max=state.max,
  )
  let rounded_width = state.width.round().to_int().max(1)
  root
  .get_style()
  .set_property("--rui-sidebar-current-width", "\{rounded_width}px")
  sidebar.get_style().set_property("transition", "none")
  sidebar
  .get_style()
  .set_property("width", "\{(rounded_width - state.gutter).max(1)}px")
  shell.get_style().set_property("transition", "none")
  shell.get_style().set_property("width", "\{rounded_width}px")
  true
}

///|
#cfg(target="js")
fn sidebar_pointer_end(event : @dom.MouseEvent) -> Int {
  guard event.to_pointer_event() is Some(pointer) else { return 0 }
  guard pointer.current_target().to_option() is Some(current_target) else {
    return 0
  }
  guard current_target.to_element() is Some(rail) else { return 0 }
  guard sidebar_pointer_state(rail) is Some(state) else { return 0 }
  guard state.pointer_id == pointer.get_pointer_id() else { return 0 }
  let width = if state.active { state.width.round().to_int().max(1) } else { 0 }
  sidebar_release_pointer_state(state, state.active)
  width
}

///|
#cfg(target="js")
fn sidebar_pointer_cancel(event : @dom.MouseEvent) -> Unit {
  guard event.to_pointer_event() is Some(pointer) else { return }
  guard pointer.current_target().to_option() is Some(current_target) else {
    return
  }
  guard current_target.to_element() is Some(rail) else { return }
  guard sidebar_pointer_state(rail) is Some(state) else { return }
  guard state.pointer_id == pointer.get_pointer_id() else { return }
  sidebar_release_pointer_state(state, false)
}

///|
fn sidebar_side_style(side : SidebarSide) -> String {
  match side {
    SidebarLeft =>
      "--rui-sidebar-rail-left:auto;--rui-sidebar-rail-right:-0.5rem;--rui-sidebar-rail-line-left:50%;border-right:1px solid var(--rui-sidebar-border,var(--rui-border,oklch(0.922 0 0)))"
    SidebarRight =>
      "--rui-sidebar-rail-left:-0.5rem;--rui-sidebar-rail-right:auto;--rui-sidebar-rail-line-left:50%;border-left:1px solid var(--rui-sidebar-border,var(--rui-border,oklch(0.922 0 0)))"
  }
}

///|
fn sidebar_shell_side_style(side : SidebarSide) -> String {
  match side {
    SidebarLeft => "order:0"
    SidebarRight => "order:1"
  }
}

///|
fn sidebar_mobile_side_style(side : SidebarSide) -> String {
  match side {
    SidebarLeft => "left:0"
    SidebarRight => "right:0"
  }
}

///|
fn sidebar_mobile_state_style(side : SidebarSide, open : Bool) -> String {
  if open {
    "transform:translateX(0);opacity:1"
  } else {
    match side {
      SidebarLeft => "transform:translateX(-100%);opacity:0"
      SidebarRight => "transform:translateX(100%);opacity:0"
    }
  }
}

///|
fn sidebar_variant_style(variant : SidebarVariant) -> String {
  match variant {
    SidebarDefault => ""
    SidebarFloating =>
      "border:1px solid var(--rui-sidebar-border,var(--rui-border,oklch(0.922 0 0)));border-radius:var(--rui-radius,0.625rem);box-shadow:0 1px 3px rgb(0 0 0 / 0.08)"
    SidebarInset => "border:0;border-radius:0;box-shadow:none"
  }
}

///|
fn sidebar_desktop_position_style(
  side : SidebarSide,
  variant : SidebarVariant,
) -> String {
  if variant is SidebarDefault {
    match side {
      SidebarLeft => "left:0"
      SidebarRight => "right:0"
    }
  } else {
    match side {
      SidebarLeft => "top:0.5rem;bottom:0.5rem;left:0.5rem"
      SidebarRight => "top:0.5rem;bottom:0.5rem;right:0.5rem"
    }
  }
}

///|
fn sidebar_desktop_surface_width_style(
  collapsed : Bool,
  collapsible : SidebarCollapsible,
  variant : SidebarVariant,
) -> String {
  if collapsed && collapsible is SidebarIcon {
    "width:var(--rui-sidebar-width-icon,3rem)"
  } else if variant is SidebarDefault {
    "width:var(--rui-sidebar-current-width,var(--rui-sidebar-width,16rem))"
  } else {
    "width:calc(var(--rui-sidebar-current-width,var(--rui-sidebar-width,16rem)) - 1rem)"
  }
}

///|
fn sidebar_desktop_state_style(
  side : SidebarSide,
  collapsed : Bool,
  collapsible : SidebarCollapsible,
  variant : SidebarVariant,
) -> String {
  if collapsed && collapsible is SidebarOffcanvas {
    if variant is SidebarDefault {
      match side {
        SidebarLeft => "transform:translateX(-100%)"
        SidebarRight => "transform:translateX(100%)"
      }
    } else {
      match side {
        SidebarLeft => "transform:translateX(calc(-100% - 0.5rem))"
        SidebarRight => "transform:translateX(calc(100% + 0.5rem))"
      }
    }
  } else {
    "transform:translateX(0)"
  }
}

///|
fn sidebar_shell_width_style(
  collapsed : Bool,
  collapsible : SidebarCollapsible,
  variant : SidebarVariant,
) -> String {
  if collapsed && collapsible is SidebarOffcanvas {
    "width:0px"
  } else if collapsed && collapsible is SidebarIcon {
    if variant is SidebarDefault {
      "width:var(--rui-sidebar-width-icon,3rem)"
    } else {
      "width:calc(var(--rui-sidebar-width-icon,3rem) + 1rem)"
    }
  } else {
    "width:var(--rui-sidebar-current-width,var(--rui-sidebar-width,16rem))"
  }
}

///|
fn sidebar_descendant_state_style(
  collapsed : Bool,
  collapsible : SidebarCollapsible,
  mobile : Bool,
  side : SidebarSide,
) -> String {
  if mobile {
    "--rui-sidebar-group-label-margin:0;--rui-sidebar-group-label-opacity:1;--rui-sidebar-group-label-pointer-events:auto;--rui-sidebar-group-action-display:inline-flex;--rui-sidebar-menu-action-display:inline-flex;--rui-sidebar-menu-badge-display:flex;--rui-sidebar-menu-sub-display:flex;--rui-sidebar-input-display:block;--rui-sidebar-content-overflow-y:auto;--rui-sidebar-item-collapse-width:100%;--rui-sidebar-item-collapse-height:var(--rui-sidebar-item-base-height,2rem);--rui-sidebar-item-collapse-gap:0.5rem;--rui-sidebar-item-collapse-font-size:var(--rui-sidebar-item-base-font-size,0.875rem);--rui-sidebar-item-justify-content:flex-start"
  } else if collapsed && collapsible is SidebarIcon {
    "--rui-sidebar-group-label-margin:-2rem;--rui-sidebar-group-label-opacity:0;--rui-sidebar-group-label-pointer-events:none;--rui-sidebar-group-action-display:none;--rui-sidebar-menu-action-display:none;--rui-sidebar-menu-badge-display:none;--rui-sidebar-menu-sub-display:none;--rui-sidebar-input-display:none;--rui-sidebar-content-overflow-y:hidden;--rui-sidebar-item-collapse-width:2rem;--rui-sidebar-item-collapse-height:2rem;--rui-sidebar-item-collapse-gap:0;--rui-sidebar-item-collapse-font-size:0;--rui-sidebar-item-justify-content:center;--rui-sidebar-item-local-padding-end:0.5rem;--rui-sidebar-badge-local-end:0.5rem;--rui-sidebar-skeleton-text-display:none;--rui-sidebar-rail-cursor:pointer"
  } else if collapsed && collapsible is SidebarOffcanvas {
    match side {
      SidebarLeft =>
        "--rui-sidebar-rail-cursor:pointer;--rui-sidebar-rail-left:auto;--rui-sidebar-rail-right:-1rem;--rui-sidebar-rail-line-left:0%"
      SidebarRight =>
        "--rui-sidebar-rail-cursor:pointer;--rui-sidebar-rail-left:-1rem;--rui-sidebar-rail-right:auto;--rui-sidebar-rail-line-left:100%"
    }
  } else {
    ""
  }
}

///|
#cfg(target="js")
const SidebarFocusableSelector : String = "a[href]:not([aria-disabled=\"true\"]):not([tabindex=\"-1\"]),button:not(:disabled):not([aria-disabled=\"true\"]):not([tabindex=\"-1\"]),input:not(:disabled):not([aria-disabled=\"true\"]):not([tabindex=\"-1\"]),select:not(:disabled):not([aria-disabled=\"true\"]):not([tabindex=\"-1\"]),textarea:not(:disabled):not([aria-disabled=\"true\"]):not([tabindex=\"-1\"]),[tabindex]:not([tabindex=\"-1\"]):not(:disabled):not([disabled]):not([aria-disabled=\"true\"])"

///|
#cfg(target="js")
priv struct SidebarModalRecord {
  node : @dom.Element
  had_inert : Bool
  inert_value : String
  had_aria_hidden : Bool
  aria_hidden : String
}

///|
#cfg(target="js")
priv struct SidebarModalState {
  root : @dom.Element
  records : Array[SidebarModalRecord]
  observer : @dom.MutationObserver?
}

///|
#cfg(target="js")
priv struct SidebarDesktopCollapseFocus {
  active : @dom.Element
  sidebar : @dom.Element
  wrapper : @dom.Element?
}

///|
#cfg(target="js")
priv struct SidebarMobileLayerBinding {
  layer : @dom.Element
  mut close_timer : Int?
}

///|
#cfg(target="js")
let sidebar_restore_trigger : Ref[@dom.Element?] = Ref(None)

///|
#cfg(target="js")
let sidebar_modal_state : Ref[SidebarModalState?] = Ref(None)

///|
#cfg(target="js")
let sidebar_desktop_collapse_focus : Ref[SidebarDesktopCollapseFocus?] = Ref(
  None,
)

///|
#cfg(target="js")
let sidebar_focus_bound_documents : Array[@dom.Document] = []

///|
#cfg(target="js")
let sidebar_mobile_layer_bindings : Array[SidebarMobileLayerBinding] = []

///|
#cfg(target="js")
fn sidebar_document_elements(selector : String) -> Array[@dom.Element] {
  let elements : Array[@dom.Element] = []
  if !ui_has_document() {
    return elements
  }
  let nodes = @dom.document().query_selector_all(selector)
  for index in 0.. Bool {
  if element.get_client_rects().length() == 0 ||
    element.closest("[inert]") is Some(_) ||
    !ui_element_is_enabled(element) {
    return false
  }
  if ui_has_window() {
    let style = @dom.window().get_computed_style(element)
    style.get_property_value("display") != "none" &&
    style.get_property_value("visibility") != "hidden"
  } else {
    true
  }
}

///|
#cfg(target="js")
fn sidebar_focusable_elements(root : @dom.Element) -> Array[@dom.Element] {
  root
  .query_selector_all(SidebarFocusableSelector)
  .filter(sidebar_element_is_visible)
}

///|
#cfg(target="js")
fn sidebar_focus_edge(root : @dom.Element, last : Bool) -> Unit {
  let items = sidebar_focusable_elements(root)
  let target = if items.length() == 0 {
    root
  } else if last {
    items[items.length() - 1]
  } else {
    items[0]
  }
  ui_focus_element_without_scroll(target)
  if target.is_same_node(root.as_node()) && ui_has_window() {
    ignore(
      @dom.window().request_animation_frame(_ => {
        if !root.get_is_connected() ||
          root.get_attribute("data-mobile-state").unwrap_or("") != "open" ||
          !ui_has_document() {
          return
        }
        let document = @dom.document()
        guard document.get_active_element() is Some(active) &&
          active.is_same_node(root.as_node()) else {
          return
        }
        let refreshed = sidebar_focusable_elements(root)
        if refreshed.length() > 0 {
          ui_focus_element_without_scroll(
            if last {
              refreshed[refreshed.length() - 1]
            } else {
              refreshed[0]
            },
          )
        }
      }),
    )
  }
}

///|
#cfg(target="js")
fn sidebar_trap_focus(event : @dom.KeyboardEvent) -> Bool {
  if event.key() != "Tab" ||
    event.alt_key() ||
    event.ctrl_key() ||
    event.meta_key() {
    return false
  }
  guard event.current_target().to_option() is Some(current) &&
    current.to_element() is Some(root) else {
    return false
  }
  if root.get_attribute("aria-hidden").unwrap_or("") == "true" {
    return false
  }
  let items = sidebar_focusable_elements(root)
  if items.length() == 0 {
    ui_focus_element_without_scroll(root)
    return true
  }
  let active = if ui_has_document() {
    @dom.document().get_active_element()
  } else {
    None
  }
  let mut active_index = -1
  if active is Some(active) {
    for index, item in items {
      if item.is_same_node(active.as_node()) {
        active_index = index
      }
    }
  }
  let next_index = if event.shift_key() {
    if active_index <= 0 {
      items.length() - 1
    } else {
      active_index - 1
    }
  } else if active_index < 0 || active_index == items.length() - 1 {
    0
  } else {
    active_index + 1
  }
  ui_focus_element_without_scroll(items[next_index])
  true
}

///|
#cfg(target="js")
fn sidebar_bind_mobile_dismiss(attrs : @html.Attrs, command : @cmd.Cmd) -> Unit {
  ignore(
    attrs.on_keydown(event => {
      if event.key() == "Escape" {
        event.prevent_default()
        command
      } else if sidebar_trap_focus(event) {
        event.prevent_default()
        @cmd.none
      } else {
        @cmd.none
      }
    }),
  )
}

///|
#cfg(target="js")
fn sidebar_current_mobile_root() -> @dom.Element? {
  let roots = sidebar_document_elements(
    "[data-slot=\"sidebar\"][data-mobile=\"true\"][data-mobile-state=\"open\"]",
  ).filter(root => {
    !root.has_attribute("hidden") &&
    root.get_attribute("aria-hidden").unwrap_or("") != "true"
  })
  roots.get(roots.length() - 1)
}

///|
#cfg(target="js")
fn sidebar_release_modal() -> Unit {
  guard sidebar_modal_state.val is Some(state) else { return }
  if state.observer is Some(observer) {
    observer.disconnect()
  }
  let mut index = state.records.length() - 1
  while index >= 0 {
    let record = state.records[index]
    if record.had_inert {
      record.node.set_attribute("inert", record.inert_value)
    } else {
      record.node.remove_attribute("inert")
    }
    if record.had_aria_hidden {
      record.node.set_attribute("aria-hidden", record.aria_hidden)
    } else {
      record.node.remove_attribute("aria-hidden")
    }
    index -= 1
  }
  sidebar_modal_state.val = None
}

///|
#cfg(target="js")
fn sidebar_contain_background(root : @dom.Element) -> Unit {
  if sidebar_modal_state.val is Some(state) &&
    state.root.is_same_node(root.as_node()) {
    return
  }
  sidebar_release_modal()
  let records : Array[SidebarModalRecord] = []
  let body = if ui_has_document() { @dom.document().get_body() } else { None }
  let mut node = root
  let mut complete = false
  while !complete {
    if node.get_parent_element() is Some(parent) {
      for sibling in parent.get_children() {
        if !sibling.is_same_node(node.as_node()) &&
          sibling.get_attribute("data-slot").unwrap_or("") != "sidebar-overlay" {
          records.push({
            node: sibling,
            had_inert: sibling.has_attribute("inert"),
            inert_value: sibling.get_attribute("inert").unwrap_or(""),
            had_aria_hidden: sibling.has_attribute("aria-hidden"),
            aria_hidden: sibling.get_attribute("aria-hidden").unwrap_or(""),
          })
          sibling.set_attribute("inert", "")
          sibling.set_attribute("aria-hidden", "true")
        }
      }
      node = parent
      complete = body is Some(body) && node.is_same_node(body.as_node())
    } else {
      complete = true
    }
  }
  let observer = @dom.MutationObserver::new((_, _) => {
    if !root.get_is_connected() ||
      root.has_attribute("hidden") ||
      root.get_attribute("aria-hidden").unwrap_or("") == "true" ||
      root.get_attribute("data-mobile-state").unwrap_or("") != "open" {
      sidebar_release_modal()
    }
  })
  if ui_has_document() &&
    @dom.document().get_document_element() is Some(document_element) {
    observer.observe(
      document_element.as_node(),
      attributes=true,
      child_list=true,
      subtree=true,
      attribute_filter=["data-mobile-state", "aria-hidden", "hidden"],
    )
  }
  sidebar_modal_state.val = Some({ root, records, observer: Some(observer) })
}

///|
#cfg(target="js")
fn sidebar_document_is_bound(document : @dom.Document) -> Bool {
  for bound in sidebar_focus_bound_documents {
    if bound.to_node().is_same_node(document.to_node()) {
      return true
    }
  }
  false
}

///|
#cfg(target="js")
fn sidebar_bind_focus_document(document : @dom.Document) -> Unit {
  if sidebar_document_is_bound(document) {
    return
  }
  sidebar_focus_bound_documents.push(document)
  document.add_event_listener_with_options(
    "focusin",
    event => {
      if sidebar_current_mobile_root() is Some(root) {
        if !ui_element_contains_target(root, event.target()) {
          sidebar_focus_edge(root, false)
        }
      } else {
        sidebar_release_modal()
      }
    },
    capture=true,
  )
  document.add_event_listener_with_options(
    "keydown",
    event => {
      guard event.to_keyboard_event() is Some(keyboard) else { return }
      guard sidebar_current_mobile_root() is Some(root) else {
        sidebar_release_modal()
        return
      }
      if keyboard.get_default_prevented() {
        return
      }
      let active_outside = document.get_active_element() is Some(active) &&
        !root.contains(active.as_node())
      if keyboard.key() == "Tab" && active_outside {
        keyboard.prevent_default()
        sidebar_focus_edge(root, keyboard.shift_key())
      } else if keyboard.key() == "Escape" &&
        !ui_element_contains_target(root, keyboard.target()) {
        keyboard.prevent_default()
        if ui_has_window() {
          // Re-dispatch the same typed event once the document dispatch completes.
          @dom.window().queue_microtask(() => {
            if root.get_is_connected() &&
              root.get_attribute("data-mobile-state").unwrap_or("") == "open" {
              root.dispatch_event(keyboard.as_event())
            }
          })
        }
      }
    },
    capture=true,
  )
}

///|
#cfg(target="js")
fn sidebar_capture_active_restore_target(
  document : @dom.Document,
  root : @dom.Element,
) -> Unit {
  if sidebar_restore_trigger.val is Some(_) {
    return
  }
  guard document.get_active_element() is Some(active) else { return }
  if root.contains(active.as_node()) || active.to_html_element() is None {
    return
  }
  if document.get_body() is Some(body) && active.is_same_node(body.as_node()) {
    return
  }
  if document.get_document_element() is Some(document_element) &&
    active.is_same_node(document_element.as_node()) {
    return
  }
  sidebar_restore_trigger.val = Some(active)
}

///|
#cfg(target="js")
fn sidebar_focus_open() -> Bool {
  if !ui_has_document() {
    return false
  }
  let document = @dom.document()
  sidebar_bind_focus_document(document)
  guard sidebar_current_mobile_root() is Some(root) else { return false }
  sidebar_capture_active_restore_target(document, root)
  sidebar_contain_background(root)
  if document.get_active_element() is None ||
    (
      document.get_active_element() is Some(active) &&
      !root.contains(active.as_node())
    ) {
    sidebar_focus_edge(root, false)
  }
  document.get_active_element() is Some(active) &&
  (root.contains(active.as_node()) || active.is_same_node(root.as_node()))
}

///|
#cfg(target="js")
fn sidebar_bind_mobile_focus(attrs : @html.Attrs, open : Bool) -> Unit {
  if open {
    ignore(
      attrs.on_focus(_ => {
        ignore(sidebar_focus_open())
        @cmd.none
      }),
    )
  }
}

///|
#cfg(not(target="js"))
fn sidebar_bind_mobile_focus(attrs : @html.Attrs, open : Bool) -> Unit {
  ignore((attrs, open))
}

///|
#cfg(not(target="js"))
fn sidebar_bind_mobile_dismiss(attrs : @html.Attrs, command : @cmd.Cmd) -> Unit {
  ignore((attrs, command))
}

///|
#cfg(target="js")
fn sidebar_bind_disabled_link(attrs : @html.Attrs, disabled : Bool) -> Unit {
  if disabled {
    ignore(
      attrs.on_click(event => {
        event.prevent_default()
        @cmd.none
      }),
    )
  }
}

///|
#cfg(not(target="js"))
fn sidebar_bind_disabled_link(attrs : @html.Attrs, disabled : Bool) -> Unit {
  ignore((attrs, disabled))
}

///|
#cfg(target="js")
fn sidebar_capture_restore_trigger(event : @dom.MouseEvent) -> Unit {
  if event.current_target().to_option() is Some(current) &&
    current.to_element() is Some(trigger) &&
    trigger.to_html_element() is Some(_) {
    sidebar_restore_trigger.val = Some(trigger)
  }
}

///|
#cfg(target="js")
fn sidebar_bind_restore_trigger(attrs : @html.Attrs, enabled : Bool) -> Unit {
  if enabled {
    ignore(
      attrs.on_click(event => {
        sidebar_capture_restore_trigger(event)
        @cmd.none
      }),
    )
  }
}

///|
#cfg(not(target="js"))
fn sidebar_bind_restore_trigger(attrs : @html.Attrs, enabled : Bool) -> Unit {
  ignore((attrs, enabled))
}

///|
#cfg(target="js")
fn sidebar_restore_captured_trigger() -> Unit {
  sidebar_release_modal()
  let trigger = sidebar_restore_trigger.val
  sidebar_restore_trigger.val = None
  guard trigger is Some(trigger) else { return }
  if trigger.get_is_connected() &&
    ui_element_is_enabled(trigger) &&
    trigger.closest("[inert],[hidden],[aria-hidden=\"true\"]") is None {
    ui_focus_element_without_scroll(trigger)
  }
}

///|
#cfg(target="js")
fn sidebar_mobile_layer_binding(
  layer : @dom.Element,
) -> SidebarMobileLayerBinding? {
  for binding in sidebar_mobile_layer_bindings {
    if binding.layer.is_same_node(layer.as_node()) {
      return Some(binding)
    }
  }
  None
}

///|
#cfg(target="js")
fn sidebar_prune_mobile_layer_bindings() -> Unit {
  let mut index = 0
  while index < sidebar_mobile_layer_bindings.length() {
    let binding = sidebar_mobile_layer_bindings[index]
    if binding.layer.get_is_connected() {
      index += 1
    } else {
      if binding.close_timer is Some(timer) && ui_has_window() {
        @dom.window().clear_timeout(timer)
      }
      ignore(sidebar_mobile_layer_bindings.remove(index))
    }
  }
}

///|
#cfg(target="js")
fn sidebar_finish_mobile_layer_close(
  binding : SidebarMobileLayerBinding,
) -> Unit {
  binding.close_timer = None
  if binding.layer.get_attribute("data-state").unwrap_or("") == "open" {
    return
  }
  if binding.layer.to_html_element() is Some(layer) &&
    binding.layer.matches(":popover-open") {
    layer.hide_popover()
  }
}

///|
#cfg(target="js")
fn sidebar_sync_mobile_layers() -> Unit {
  if !ui_has_document() {
    return
  }
  sidebar_prune_mobile_layer_bindings()
  let layers = sidebar_document_elements(
    "[data-slot=\"sidebar-mobile-layer\"][popover]",
  )
  for layer in layers {
    let binding = if sidebar_mobile_layer_binding(layer) is Some(binding) {
      binding
    } else {
      let binding : SidebarMobileLayerBinding = { layer, close_timer: None }
      sidebar_mobile_layer_bindings.push(binding)
      binding
    }
    let requested = layer.get_attribute("data-state").unwrap_or("") == "open"
    let native_open = layer.matches(":popover-open")
    if requested {
      if binding.close_timer is Some(timer) {
        if ui_has_window() {
          @dom.window().clear_timeout(timer)
        }
        binding.close_timer = None
      }
      if !native_open && layer.to_html_element() is Some(html) {
        html.show_popover()
      }
    } else if native_open && binding.close_timer is None {
      if ui_has_window() {
        binding.close_timer = Some(
          @dom.window().set_timeout(
            () => sidebar_finish_mobile_layer_close(binding),
            200,
          ),
        )
      } else {
        sidebar_finish_mobile_layer_close(binding)
      }
    }
  }
}

///|
#cfg(target="js")
fn sidebar_mobile_effect_cmd(open : Bool, restore_on_close : Bool) -> @cmd.Cmd {
  @cmd.custom_cmd(kind=@cmd.AfterRender, _ => {
    sidebar_sync_mobile_layers()
    if open {
      ignore(sidebar_focus_open())
    } else if restore_on_close && !sidebar_focus_open() {
      sidebar_restore_captured_trigger()
    }
  })
}

///|
#cfg(not(target="js"))
fn sidebar_mobile_effect_cmd(open : Bool, restore_on_close : Bool) -> @cmd.Cmd {
  ignore((open, restore_on_close))
  @cmd.none
}

///|
#cfg(target="js")
fn sidebar_capture_desktop_collapse_focus() -> Unit {
  sidebar_desktop_collapse_focus.val = None
  if !ui_has_document() {
    return
  }
  guard @dom.document().get_active_element() is Some(active) else { return }
  guard active.closest("[data-slot=\"sidebar\"][data-mobile=\"false\"]")
    is Some(sidebar) else {
    return
  }
  if active.get_attribute("data-slot").unwrap_or("") == "sidebar-rail" {
    return
  }
  sidebar_desktop_collapse_focus.val = Some({
    active,
    sidebar,
    wrapper: sidebar.closest("[data-slot=\"sidebar-wrapper\"]"),
  })
}

///|
#cfg(target="js")
fn sidebar_restore_desktop_collapse_focus() -> Unit {
  let captured = sidebar_desktop_collapse_focus.val
  sidebar_desktop_collapse_focus.val = None
  guard captured is Some(state) else { return }
  if !ui_has_document() ||
    !state.sidebar.get_is_connected() ||
    state.sidebar.get_attribute("data-state").unwrap_or("") != "collapsed" {
    return
  }
  let document = @dom.document()
  guard document.get_active_element() is Some(current) else { return }
  let active_still_current = current.is_same_node(state.active.as_node())
  let body_is_current = document.get_body() is Some(body) &&
    current.is_same_node(body.as_node())
  if !active_still_current && !body_is_current {
    return
  }
  let hidden = !state.active.get_is_connected() ||
    state.sidebar.get_attribute("data-collapsible").unwrap_or("") == "offcanvas" ||
    state.active.get_client_rects().length() == 0 ||
    (
      ui_has_window() &&
      (
        @dom.window()
        .get_computed_style(state.active)
        .get_property_value("display") ==
        "none" ||
        @dom.window()
        .get_computed_style(state.active)
        .get_property_value("visibility") ==
        "hidden"
      )
    )
  if !hidden {
    return
  }
  let trigger = if state.wrapper is Some(wrapper) {
    wrapper.query_selector(
      "[data-slot=\"sidebar-trigger\"][aria-expanded=\"false\"]:not(:disabled):not([aria-disabled=\"true\"])",
    )
  } else {
    None
  }
  let target = if trigger is Some(trigger) {
    Some(trigger)
  } else {
    state.sidebar.query_selector("[data-slot=\"sidebar-rail\"]")
  }
  if target is Some(target) {
    ui_focus_element_without_scroll(target)
  }
}

///|
#cfg(target="js")
fn sidebar_desktop_collapse_focus_cmd() -> @cmd.Cmd {
  @cmd.batch([
    @cmd.custom_cmd(kind=@cmd.Immediately, _ => {
      sidebar_capture_desktop_collapse_focus()
    }),
    @cmd.custom_cmd(kind=@cmd.AfterRender, _ => {
      sidebar_restore_desktop_collapse_focus()
    }),
  ])
}

///|
#cfg(not(target="js"))
fn sidebar_desktop_collapse_focus_cmd() -> @cmd.Cmd {
  @cmd.none
}

///|
fn sidebar_mobile_change_cmd(
  notify : @cmd.Emit[Bool]?,
  open : Bool,
) -> @cmd.Cmd {
  @cmd.batch([
    sidebar_emit_bool(notify, open),
    sidebar_mobile_effect_cmd(open, true),
  ])
}

///|
fn sidebar_provider_width_style(width : Int?) -> String {
  match width {
    Some(width) => "--rui-sidebar-current-width:\{width}px"
    None => ""
  }
}

///|
fn sidebar_menu_size_value(size : SidebarMenuButtonSize) -> String {
  match size {
    SidebarMenuSm => "sm"
    SidebarMenuDefault => "default"
    SidebarMenuLg => "lg"
  }
}

///|
fn sidebar_menu_variant_value(variant : SidebarMenuButtonVariant) -> String {
  match variant {
    SidebarMenuButtonDefault => "default"
    SidebarMenuButtonOutline => "outline"
  }
}

///|
fn sidebar_menu_variant_style(variant : SidebarMenuButtonVariant) -> String {
  match variant {
    SidebarMenuButtonDefault => ""
    SidebarMenuButtonOutline => SidebarMenuButtonOutlineStyle
  }
}

///|
fn sidebar_menu_size_style(size : SidebarMenuButtonSize) -> String {
  match size {
    SidebarMenuSm =>
      "height:var(--rui-sidebar-item-collapse-height,1.75rem);--rui-sidebar-item-base-height:1.75rem;--rui-sidebar-item-base-font-size:0.75rem"
    SidebarMenuDefault =>
      "height:var(--rui-sidebar-item-collapse-height,2rem);--rui-sidebar-item-base-height:2rem;--rui-sidebar-item-base-font-size:0.875rem"
    SidebarMenuLg =>
      "height:var(--rui-sidebar-item-collapse-height,3rem);--rui-sidebar-item-base-height:3rem;--rui-sidebar-item-base-font-size:0.875rem"
  }
}

///|
fn sidebar_emit_bool(emit : @cmd.Emit[Bool]?, value : Bool) -> @cmd.Cmd {
  if emit is Some(notify) {
    notify(value)
  } else {
    @cmd.none
  }
}

///|
#cfg(target="js")
fn sidebar_scope(
  state : SidebarState,
  emit : @cmd.Emit[SidebarMessage],
) -> SidebarScope {
  {
    collapsed: state.collapsed,
    mobile_open: state.mobile_open,
    desktop_width: state.desktop_width,
    toggle_collapsed: emit(ToggleSidebarCollapsed),
    expand: emit(SetSidebarCollapsed(false)),
    collapse: emit(SetSidebarCollapsed(true)),
    toggle_mobile: emit(ToggleSidebarMobile),
    open_mobile: emit(SetSidebarMobile(true)),
    close_mobile: emit(SetSidebarMobile(false)),
    commit_width: emit.map(width => CommitSidebarWidth(width)),
  }
}

///|
fn sidebar_provider_view(
  scope : SidebarScope,
  id : String?,
  class : String?,
  title : String?,
  attrs : @html.Attrs?,
  style : Array[String],
  children : (SidebarScope) -> @html.Html,
) -> @html.Html {
  let element_attrs = ui_attrs(attrs)
    .data_set("slot", "sidebar-wrapper")
    .data_set("state", sidebar_state_value(scope.collapsed))
    .data_set("mobile", if scope.mobile_open { "open" } else { "closed" })
    .data_set(
      "width",
      match scope.desktop_width {
        Some(width) => "\{width}"
        None => "auto"
      },
    )
  @html.div(
    style=ui_styles(
      [
        UiBoxSizing,
        UiFontSans,
        UiTextRendering,
        SidebarProviderStyle,
        sidebar_provider_width_style(scope.desktop_width),
      ],
      style,
    ),
    id?,
    class?,
    title?,
    attrs=element_attrs,
    children(scope),
  )
}

///|
/// Own desktop-collapse and mobile-open state for a complete sidebar tree.
/// The native fallback renders the requested initial state as a constant value.
#cfg(target="js")
pub fn sidebar_provider(
  default_collapsed? : Bool = false,
  default_mobile_open? : Bool = false,
  on_collapsed_change? : @cmd.Emit[Bool],
  on_mobile_open_change? : @cmd.Emit[Bool],
  id? : String,
  class? : String,
  title? : String,
  attrs? : @html.Attrs,
  style? : Array[String] = [],
  children : (SidebarScope) -> @html.Html,
) -> @rabbita.Val[@html.Html] {
  let initial : SidebarState = {
    collapsed: default_collapsed,
    mobile_open: default_mobile_open,
    desktop_width: None,
  }
  let (state, emit) = @rabbita.create_state_with_init(
    init=fn(_) {
      (
        initial,
        if default_mobile_open {
          sidebar_mobile_effect_cmd(true, false)
        } else {
          @cmd.none
        },
      )
    },
    update=fn(_, msg, state) {
      match msg {
        ToggleSidebarCollapsed => {
          let next = !state.collapsed
          (
            { ..state, collapsed: next },
            @cmd.batch([
              sidebar_emit_bool(on_collapsed_change, next),
              if next {
                sidebar_desktop_collapse_focus_cmd()
              } else {
                @cmd.none
              },
            ]),
          )
        }
        SetSidebarCollapsed(next) =>
          if next == state.collapsed {
            (state, @cmd.none)
          } else {
            (
              { ..state, collapsed: next },
              @cmd.batch([
                sidebar_emit_bool(on_collapsed_change, next),
                if next {
                  sidebar_desktop_collapse_focus_cmd()
                } else {
                  @cmd.none
                },
              ]),
            )
          }
        ToggleSidebarMobile => {
          let next = !state.mobile_open
          (
            { ..state, mobile_open: next },
            sidebar_mobile_change_cmd(on_mobile_open_change, next),
          )
        }
        SetSidebarMobile(next) =>
          if next == state.mobile_open {
            (state, @cmd.none)
          } else {
            (
              { ..state, mobile_open: next },
              sidebar_mobile_change_cmd(on_mobile_open_change, next),
            )
          }
        CommitSidebarWidth(width) => {
          let next = { ..state, collapsed: false, desktop_width: Some(width) }
          if next == state {
            (state, @cmd.none)
          } else {
            (
              next,
              if state.collapsed {
                sidebar_emit_bool(on_collapsed_change, false)
              } else {
                @cmd.none
              },
            )
          }
        }
      }
    },
  )
  state.view(state => {
    sidebar_provider_view(
      sidebar_scope(state, emit),
      id,
      class,
      title,
      attrs,
      style,
      children,
    )
  })
}

///|
#cfg(not(target="js"))
pub fn sidebar_provider(
  default_collapsed? : Bool = false,
  default_mobile_open? : Bool = false,
  on_collapsed_change? : @cmd.Emit[Bool],
  on_mobile_open_change? : @cmd.Emit[Bool],
  id? : String,
  class? : String,
  title? : String,
  attrs? : @html.Attrs,
  style? : Array[String] = [],
  children : (SidebarScope) -> @html.Html,
) -> @rabbita.Val[@html.Html] {
  ignore((on_collapsed_change, on_mobile_open_change))
  let scope : SidebarScope = {
    collapsed: default_collapsed,
    mobile_open: default_mobile_open,
    desktop_width: None,
    toggle_collapsed: @cmd.none,
    expand: @cmd.none,
    collapse: @cmd.none,
    toggle_mobile: @cmd.none,
    open_mobile: @cmd.none,
    close_mobile: @cmd.none,
    commit_width: @cmd.Emit(_ => @cmd.none),
  }
  @rabbita.Val::constant(
    sidebar_provider_view(scope, id, class, title, attrs, style, children),
  )
}

///|
pub fn sidebar_is_collapsed(scope : SidebarScope) -> Bool {
  scope.collapsed
}

///|
pub fn sidebar_is_mobile_open(scope : SidebarScope) -> Bool {
  scope.mobile_open
}

///|
/// Commands for applications that need custom controls instead of the default
/// trigger and rail.
pub fn sidebar_expand(scope : SidebarScope) -> @cmd.Cmd {
  scope.expand
}

///|
pub fn sidebar_collapse(scope : SidebarScope) -> @cmd.Cmd {
  scope.collapse
}

///|
pub fn sidebar_open_mobile(scope : SidebarScope) -> @cmd.Cmd {
  scope.open_mobile
}

///|
pub fn sidebar_close_mobile(scope : SidebarScope) -> @cmd.Cmd {
  scope.close_mobile
}

///|
/// Render either the desktop sidebar or its explicit mobile sheet form.
/// `mobile=true` is intentional: inline styles have no hidden stylesheet or
/// breakpoint registration, so applications choose when to switch forms.
pub fn[C : @html.IsChildren] sidebar(
  scope : SidebarScope,
  side? : SidebarSide = SidebarLeft,
  variant? : SidebarVariant = SidebarDefault,
  collapsible? : SidebarCollapsible = SidebarOffcanvas,
  mobile? : Bool = false,
  aria_label? : String = "Sidebar",
  id? : String,
  class? : String,
  title? : String,
  hidden? : Bool = false,
  attrs? : @html.Attrs,
  style? : Array[String] = [],
  overlay_attrs? : @html.Attrs,
  overlay_style? : Array[String] = [],
  children : C,
) -> @html.Html {
  let collapsed = if mobile || collapsible == SidebarFixed {
    false
  } else {
    scope.collapsed
  }
  let is_hidden = hidden
  let mobile_open = scope.mobile_open && !is_hidden
  let element_attrs = ui_attrs(attrs)
    .data_set("slot", "sidebar")
    .data_set("side", sidebar_side_value(side))
    .data_set("variant", sidebar_variant_value(variant))
    .data_set("collapsible", sidebar_collapsible_value(collapsible))
    .data_set("state", sidebar_state_value(collapsed))
    .data_set("mobile", ui_bool(mobile))
    .data_set("mobile-state", if mobile_open { "open" } else { "closed" })
    .aria_label(aria_label)
  if mobile {
    ignore(
      element_attrs
      .role("dialog")
      .aria_modal("true")
      .aria_hidden(ui_bool(!mobile_open))
      .inert(!mobile_open)
      .autofocus(mobile_open)
      .tabindex(-1),
    )
    sidebar_bind_mobile_dismiss(element_attrs, scope.close_mobile)
    sidebar_bind_mobile_focus(element_attrs, mobile_open)
  }
  let surface = @html.aside(
    style=ui_styles(
      [
        UiBoxSizing,
        UiFontSans,
        UiTextRendering,
        SidebarBaseStyle,
        if mobile {
          SidebarMobileStyle
        } else {
          SidebarDesktopStyle
        },
        if mobile {
          sidebar_mobile_side_style(side)
        } else {
          ""
        },
        if mobile {
          sidebar_mobile_state_style(side, mobile_open)
        } else {
          ""
        },
        sidebar_side_style(side),
        if mobile {
          ""
        } else {
          sidebar_variant_style(variant)
        },
        if mobile {
          ""
        } else {
          sidebar_desktop_position_style(side, variant)
        },
        if mobile {
          ""
        } else {
          sidebar_desktop_surface_width_style(collapsed, collapsible, variant)
        },
        if mobile {
          ""
        } else {
          sidebar_desktop_state_style(side, collapsed, collapsible, variant)
        },
        sidebar_descendant_state_style(collapsed, collapsible, mobile, side),
      ],
      style,
    ),
    id?,
    class?,
    title?,
    hidden=is_hidden,
    attrs=element_attrs,
    children,
  )
  if mobile {
    let backdrop_attrs = ui_attrs(overlay_attrs)
      .data_set("slot", "sidebar-overlay")
      .data_set("state", if mobile_open { "open" } else { "closed" })
      .aria_hidden("true")
    ignore(backdrop_attrs.on_click(_ => scope.close_mobile))
    let layer_attrs = @html.Attrs::build()
      .data_set("slot", "sidebar-mobile-layer")
      .data_set("state", if mobile_open { "open" } else { "closed" })
      .popover("manual")
    @html.div(
      style=[UiBoxSizing, UiFontSans, SidebarMobileLayerStyle],
      attrs=layer_attrs,
      [
        @html.div(
          style=ui_styles(
            [
              UiBoxSizing,
              SidebarOverlayStyle,
              if mobile_open {
                SidebarOverlayOpenStyle
              } else {
                SidebarOverlayClosedStyle
              },
            ],
            overlay_style,
          ),
          attrs=backdrop_attrs,
          @html.nothing,
        ),
        surface,
      ],
    )
  } else {
    let shell_attrs = @html.Attrs::build()
      .data_set("slot", "sidebar-shell")
      .data_set("side", sidebar_side_value(side))
      .data_set("variant", sidebar_variant_value(variant))
      .data_set("collapsible", sidebar_collapsible_value(collapsible))
      .data_set("state", sidebar_state_value(collapsed))
    @html.div(
      style=[
        UiBoxSizing,
        SidebarShellStyle,
        sidebar_shell_side_style(side),
        sidebar_shell_width_style(collapsed, collapsible, variant),
      ],
      hidden=is_hidden,
      attrs=shell_attrs,
      surface,
    )
  }
}

///|
pub fn[C : @html.IsChildren] sidebar_trigger(
  scope : SidebarScope,
  mobile? : Bool = false,
  controls? : String,
  disabled? : Bool = false,
  id? : String,
  class? : String,
  title? : String,
  aria_label? : String = "Toggle sidebar",
  on_click? : @cmd.Cmd,
  attrs? : @html.Attrs,
  style? : Array[String] = [],
  children : C,
) -> @html.Html {
  let expanded = if mobile { scope.mobile_open } else { !scope.collapsed }
  let trigger_attrs = ui_attrs(attrs)
    .data_set("slot", "sidebar-trigger")
    .data_set("state", if expanded { "open" } else { "closed" })
    .aria_expanded(ui_bool(expanded))
  if controls is Some(value) {
    ignore(trigger_attrs.aria_controls(value))
  }
  sidebar_bind_restore_trigger(
    trigger_attrs,
    mobile && !scope.mobile_open && !disabled,
  )
  dialog_control_button(
    slot="sidebar-trigger",
    command=if mobile { scope.toggle_mobile } else { scope.toggle_collapsed },
    variant=Ghost,
    size=IconSm,
    disabled~,
    id~,
    class~,
    title~,
    aria_label=Some(aria_label),
    on_click~,
    attrs=Some(trigger_attrs),
    style=ui_styles(
      ["width:1.75rem;height:1.75rem;--rui-button-base-shadow:0 0 #0000"],
      style,
    ),
    children,
  )
}

///|
#cfg(target="js")
fn sidebar_bind_resize_attrs(
  attrs : @html.Attrs,
  scope : SidebarScope,
  min_width : Int,
  max_width : Int,
) -> Unit {
  ignore(
    attrs
    .on_pointerdown(event => {
      ignore(sidebar_pointer_start(event, min_width, max_width))
      @cmd.none
    })
    .on_pointermove(event => {
      if sidebar_pointer_move(event) {
        event.prevent_default()
      }
      @cmd.none
    })
    .on_pointerup(event => {
      let width = sidebar_pointer_end(event)
      if width > 0 {
        event.prevent_default()
        (scope.commit_width)(width)
      } else {
        @cmd.none
      }
    })
    .on_pointercancel(event => {
      sidebar_pointer_cancel(event)
      @cmd.none
    }),
  )
}

///|
#cfg(not(target="js"))
fn sidebar_bind_resize_attrs(
  attrs : @html.Attrs,
  scope : SidebarScope,
  min_width : Int,
  max_width : Int,
) -> Unit {
  ignore((attrs, scope, min_width, max_width))
}

///|
/// Render the desktop edge control for toggling and pointer resizing.
/// Placement and resize direction are derived from the nearest sidebar.
pub fn sidebar_rail(
  scope : SidebarScope,
  min_width? : Int = 160,
  max_width? : Int = 512,
  id? : String,
  class? : String,
  title? : String = "Toggle sidebar",
  aria_label? : String = "Toggle sidebar",
  on_click? : @cmd.Cmd,
  attrs? : @html.Attrs,
  style? : Array[String] = [],
) -> @html.Html {
  let line_edge = "transform:translateX(-50%)"
  let rail_attrs = ui_attrs(attrs)
    .data_set("slot", "sidebar-rail")
    .data_set("min-width", "\{min_width}")
    .data_set("max-width", "\{max_width}")
    .aria_expanded(ui_bool(!scope.collapsed))
    .tabindex(-1)
  ignore(
    rail_attrs.on_click(_ => {
      @cmd.batch([
        match on_click {
          Some(command) => command
          None => @cmd.none
        },
        scope.toggle_collapsed,
      ])
    }),
  )
  sidebar_bind_resize_attrs(rail_attrs, scope, min_width, max_width)
  dialog_control_button(
    slot="sidebar-rail",
    command=@cmd.none,
    variant=Ghost,
    size=IconXs,
    disabled=false,
    id~,
    class~,
    title=Some(title),
    aria_label=Some(aria_label),
    on_click=None,
    attrs=Some(rail_attrs),
    style=ui_styles([SidebarRailStyle], style),
    @html.span(
      attrs=@html.Attrs::build()
        .data_set("slot", "sidebar-rail-line")
        .aria_hidden("true"),
      style=[SidebarRailLineStyle, line_edge],
      @html.nothing,
    ),
  )
}

///|
pub fn[C : @html.IsChildren] sidebar_header(
  id? : String,
  class? : String,
  title? : String,
  attrs? : @html.Attrs,
  style? : Array[String] = [],
  children : C,
) -> @html.Html {
  @html.div(
    style=ui_styles([UiBoxSizing, SidebarHeaderFooterStyle], style),
    id?,
    class?,
    title?,
    attrs=ui_attrs(attrs).data_set("slot", "sidebar-header"),
    children,
  )
}

///|
pub fn[C : @html.IsChildren] sidebar_footer(
  id? : String,
  class? : String,
  title? : String,
  attrs? : @html.Attrs,
  style? : Array[String] = [],
  children : C,
) -> @html.Html {
  @html.div(
    style=ui_styles([UiBoxSizing, SidebarHeaderFooterStyle], style),
    id?,
    class?,
    title?,
    attrs=ui_attrs(attrs).data_set("slot", "sidebar-footer"),
    children,
  )
}

///|
pub fn[C : @html.IsChildren] sidebar_content(
  id? : String,
  class? : String,
  title? : String,
  attrs? : @html.Attrs,
  style? : Array[String] = [],
  children : C,
) -> @html.Html {
  @html.div(
    style=ui_styles([UiBoxSizing, SidebarContentStyle], style),
    id?,
    class?,
    title?,
    attrs=ui_attrs(attrs).data_set("slot", "sidebar-content"),
    children,
  )
}

///|
pub fn[C : @html.IsChildren] sidebar_group(
  id? : String,
  class? : String,
  title? : String,
  aria_label? : String,
  attrs? : @html.Attrs,
  style? : Array[String] = [],
  children : C,
) -> @html.Html {
  let element_attrs = ui_attrs(attrs).data_set("slot", "sidebar-group")
  if aria_label is Some(label) {
    ignore(element_attrs.role("group").aria_label(label))
  }
  @html.div(
    style=ui_styles([UiBoxSizing, SidebarGroupStyle], style),
    id?,
    class?,
    title?,
    attrs=element_attrs,
    children,
  )
}

///|
pub fn[C : @html.IsChildren] sidebar_group_label(
  scope? : SidebarScope,
  hide_when_collapsed? : Bool = true,
  id? : String,
  class? : String,
  title? : String,
  attrs? : @html.Attrs,
  style? : Array[String] = [],
  children : C,
) -> @html.Html {
  // The owning sidebar knows whether its collapse mode actually hides labels.
  // Rely on that inherited custom property so SidebarFixed never hides a label
  // merely because the provider also drives a different collapsed sidebar.
  ignore(scope)
  @html.div(
    style=ui_styles(
      [
        UiBoxSizing,
        SidebarGroupLabelStyle,
        if hide_when_collapsed {
          ""
        } else {
          "--rui-sidebar-group-label-local-margin:0;--rui-sidebar-group-label-local-opacity:1;--rui-sidebar-group-label-local-pointer-events:auto"
        },
      ],
      style,
    ),
    id?,
    class?,
    title?,
    attrs=ui_attrs(attrs)
      .data_set("slot", "sidebar-group-label")
      .data_set("state", if hide_when_collapsed { "auto" } else { "visible" }),
    children,
  )
}

///|
pub fn[C : @html.IsChildren] sidebar_group_action(
  disabled? : Bool = false,
  id? : String,
  class? : String,
  title? : String,
  aria_label? : String,
  on_click? : @cmd.Cmd,
  attrs? : @html.Attrs,
  style? : Array[String] = [],
  children : C,
) -> @html.Html {
  dialog_control_button(
    slot="sidebar-group-action",
    command=@cmd.none,
    variant=Ghost,
    size=IconXs,
    disabled~,
    id~,
    class~,
    title~,
    aria_label~,
    on_click~,
    attrs~,
    style=ui_styles(
      [
        SidebarGroupActionStyle,
        SidebarActionSurfaceStyle,
        SidebarActionSizeStyle,
      ],
      style,
    ),
    children,
  )
}

///|
pub fn[C : @html.IsChildren] sidebar_group_content(
  id? : String,
  class? : String,
  title? : String,
  attrs? : @html.Attrs,
  style? : Array[String] = [],
  children : C,
) -> @html.Html {
  @html.div(
    style=ui_styles([UiBoxSizing, SidebarGroupContentStyle], style),
    id?,
    class?,
    title?,
    attrs=ui_attrs(attrs).data_set("slot", "sidebar-group-content"),
    children,
  )
}

///|
pub fn[C : @html.IsChildren] sidebar_menu(
  id? : String,
  class? : String,
  title? : String,
  attrs? : @html.Attrs,
  style? : Array[String] = [],
  children : C,
) -> @html.Html {
  @html.ul(
    style=ui_styles([UiBoxSizing, SidebarMenuStyle], style),
    id?,
    class?,
    title?,
    attrs=ui_attrs(attrs).data_set("slot", "sidebar-menu"),
    children,
  )
}

///|
pub fn[C : @html.IsChildren] sidebar_menu_item(
  id? : String,
  class? : String,
  title? : String,
  attrs? : @html.Attrs,
  style? : Array[String] = [],
  children : C,
) -> @html.Html {
  @html.li(
    style=ui_styles([UiBoxSizing, SidebarMenuItemStyle], style),
    id?,
    class?,
    title?,
    attrs=ui_attrs(attrs).data_set("slot", "sidebar-menu-item"),
    children,
  )
}

///|
fn[C : @html.IsChildren] render_sidebar_menu_button(
  href : String?,
  is_active : Bool,
  variant : SidebarMenuButtonVariant,
  size : SidebarMenuButtonSize,
  disabled : Bool,
  aria_label : String?,
  target : @html.Target,
  rel : String?,
  download : String?,
  on_click : @cmd.Cmd?,
  id : String?,
  class_ : String?,
  title : String?,
  attrs : @html.Attrs?,
  style : Array[String],
  children : C,
) -> @html.Html {
  let element_attrs = ui_attrs(attrs)
    .data_set("slot", "sidebar-menu-button")
    .data_set("variant", sidebar_menu_variant_value(variant))
    .data_set("size", sidebar_menu_size_value(size))
    .data_set("active", ui_bool(is_active))
    .data_set("state", if is_active { "active" } else { "inactive" })
  if aria_label is Some(label) {
    ignore(element_attrs.aria_label(label))
  }
  if is_active && href is Some(_) {
    ignore(element_attrs.aria_current("page"))
  }
  if disabled {
    ignore(
      element_attrs.aria_disabled("true").data_set("disabled", "").tabindex(-1),
    )
  } else if on_click is Some(command) {
    ignore(element_attrs.on_click(_ => command))
  }
  sidebar_bind_disabled_link(element_attrs, disabled && href is Some(_))
  let styles = ui_styles(
    [
      UiBoxSizing,
      UiFontSans,
      UiTextRendering,
      UiTransition,
      SidebarMenuButtonStyle,
      sidebar_menu_variant_style(variant),
      sidebar_menu_size_style(size),
      if is_active {
        SidebarMenuButtonActiveStyle
      } else {
        ""
      },
      ui_disabled_style(disabled),
    ],
    style,
  )
  match href {
    Some(href) =>
      @html.a(
        style=styles,
        id?,
        class?=class_,
        title?,
        href~,
        target~,
        rel?,
        download?,
        attrs=element_attrs,
        children,
      )
    None =>
      @html.button(
        style=styles,
        id?,
        class?=class_,
        title?,
        type_="button",
        disabled~,
        attrs=element_attrs,
        children,
      )
  }
}

///|
/// Render a native button, or a native anchor when `href` is supplied.
pub fn[C : @html.IsChildren] sidebar_menu_button(
  href? : String,
  is_active? : Bool = false,
  variant? : SidebarMenuButtonVariant = SidebarMenuButtonDefault,
  size? : SidebarMenuButtonSize = SidebarMenuDefault,
  disabled? : Bool = false,
  aria_label? : String,
  target? : @html.Target = Self,
  rel? : String,
  download? : String,
  on_click? : @cmd.Cmd,
  id? : String,
  class? : String,
  title? : String,
  attrs? : @html.Attrs,
  style? : Array[String] = [],
  children : C,
) -> @html.Html {
  render_sidebar_menu_button(
    href, is_active, variant, size, disabled, aria_label, target, rel, download,
    on_click, id, class, title, attrs, style, children,
  )
}

///|
fn[C : @html.IsChildren] render_sidebar_menu_button_with_tooltip(
  id : String,
  tooltip : String,
  open : Bool,
  href : String?,
  is_active : Bool,
  variant : SidebarMenuButtonVariant,
  size : SidebarMenuButtonSize,
  disabled : Bool,
  aria_label : String?,
  target : @html.Target,
  rel : String?,
  download : String?,
  on_click : @cmd.Cmd?,
  class_ : String?,
  title : String?,
  attrs : @html.Attrs?,
  style : Array[String],
  tooltip_side : PopupSide,
  tooltip_align : PopupAlign,
  tooltip_side_offset : Int,
  tooltip_align_offset : Int,
  tooltip_show_arrow : Bool,
  tooltip_attrs : @html.Attrs?,
  tooltip_style : Array[String],
  children : C,
) -> @html.Html {
  let content_id = id + "-tooltip"
  let trigger_attrs = ui_attrs(attrs)
    .data_set("tooltip-root", "")
    .data_set("tooltip-state", if open { "open" } else { "closed" })
    .data_set("close-on-click", "true")
    .data_set("hoverable", "true")
    .data_set("sidebar-collapsed-only", "true")
  if open {
    ignore(trigger_attrs.aria_describedby(content_id))
  }
  let content_attrs = ui_attrs(tooltip_attrs).data_set("tooltip-owner-id", id)
  @html.fragment([
    render_sidebar_menu_button(
      href,
      is_active,
      variant,
      size,
      disabled,
      aria_label,
      target,
      rel,
      download,
      on_click,
      Some(id),
      class_,
      title,
      Some(trigger_attrs),
      style,
      children,
    ),
    tooltip_content(
      id=content_id,
      trigger_id=id,
      open~,
      side=tooltip_side,
      align=tooltip_align,
      side_offset=tooltip_side_offset,
      align_offset=tooltip_align_offset,
      show_arrow=tooltip_show_arrow,
      attrs=content_attrs,
      style=tooltip_style,
      tooltip,
    ),
  ])
}

///|
/// Render a sidebar menu button with a tooltip that is active only while its
/// desktop icon sidebar is collapsed.
///
/// The tooltip reuses the button or anchor as its trigger, so this API never
/// nests interactive elements. Expanded, off-canvas, fixed, and mobile
/// sidebars suppress the tooltip automatically.
#cfg(target="js")
pub fn[C : @html.IsChildren] sidebar_menu_button_with_tooltip(
  id~ : String,
  tooltip~ : String,
  href? : String,
  is_active? : Bool = false,
  variant? : SidebarMenuButtonVariant = SidebarMenuButtonDefault,
  size? : SidebarMenuButtonSize = SidebarMenuDefault,
  disabled? : Bool = false,
  aria_label? : String,
  target? : @html.Target = Self,
  rel? : String,
  download? : String,
  on_click? : @cmd.Cmd,
  on_tooltip_open_change? : @cmd.Emit[Bool],
  class? : String,
  title? : String,
  attrs? : @html.Attrs,
  style? : Array[String] = [],
  tooltip_side? : PopupSide = Right,
  tooltip_align? : PopupAlign = Center,
  tooltip_side_offset? : Int = 4,
  tooltip_align_offset? : Int = 0,
  tooltip_show_arrow? : Bool = true,
  tooltip_attrs? : @html.Attrs,
  tooltip_style? : Array[String] = [],
  children : C,
) -> @rabbita.Val[@html.Html] {
  let content_id = id + "-tooltip"
  let initial_model : TooltipModel = {
    open: false,
    trigger_hovered: false,
    trigger_focused: false,
    content_hovered: false,
    generation: 0,
  }
  let (model, _) = @rabbita.create_state_with_init(
    init=emit => {
      let sync = emit.map(value => TooltipNativeChanged(value))
      (
        initial_model,
        @cmd.batch([
          floating_bind_sync_cmd(content_id, sync),
          tooltip_bind_trigger_events_cmd(content_id, id, id, emit),
        ]),
      )
    },
    update=(emit, msg, current) => {
      tooltip_update(
        content_id, true, on_tooltip_open_change, emit, msg, current,
      )
    },
  )
  model.view(model => {
    render_sidebar_menu_button_with_tooltip(
      id,
      tooltip,
      model.open,
      href,
      is_active,
      variant,
      size,
      disabled,
      aria_label,
      target,
      rel,
      download,
      on_click,
      class,
      title,
      attrs,
      style,
      tooltip_side,
      tooltip_align,
      tooltip_side_offset,
      tooltip_align_offset,
      tooltip_show_arrow,
      tooltip_attrs,
      tooltip_style,
      children,
    )
  })
}

///|
#cfg(not(target="js"))
pub fn[C : @html.IsChildren] sidebar_menu_button_with_tooltip(
  id~ : String,
  tooltip~ : String,
  href? : String,
  is_active? : Bool = false,
  variant? : SidebarMenuButtonVariant = SidebarMenuButtonDefault,
  size? : SidebarMenuButtonSize = SidebarMenuDefault,
  disabled? : Bool = false,
  aria_label? : String,
  target? : @html.Target = Self,
  rel? : String,
  download? : String,
  on_click? : @cmd.Cmd,
  on_tooltip_open_change? : @cmd.Emit[Bool],
  class? : String,
  title? : String,
  attrs? : @html.Attrs,
  style? : Array[String] = [],
  tooltip_side? : PopupSide = Right,
  tooltip_align? : PopupAlign = Center,
  tooltip_side_offset? : Int = 4,
  tooltip_align_offset? : Int = 0,
  tooltip_show_arrow? : Bool = true,
  tooltip_attrs? : @html.Attrs,
  tooltip_style? : Array[String] = [],
  children : C,
) -> @rabbita.Val[@html.Html] {
  ignore(on_tooltip_open_change)
  @rabbita.Val::constant(
    render_sidebar_menu_button_with_tooltip(
      id, tooltip, false, href, is_active, variant, size, disabled, aria_label, target,
      rel, download, on_click, class, title, attrs, style, tooltip_side, tooltip_align,
      tooltip_side_offset, tooltip_align_offset, tooltip_show_arrow, tooltip_attrs,
      tooltip_style, children,
    ),
  )
}

///|
pub fn[C : @html.IsChildren] sidebar_menu_action(
  show_on_hover? : Bool = false,
  disabled? : Bool = false,
  id? : String,
  class? : String,
  title? : String,
  aria_label? : String,
  on_click? : @cmd.Cmd,
  attrs? : @html.Attrs,
  style? : Array[String] = [],
  children : C,
) -> @html.Html {
  let element_attrs = ui_attrs(attrs)
    .data_set("slot", "sidebar-menu-action")
    .data_set("show-on-hover", ui_bool(show_on_hover))
  dialog_control_button(
    slot="sidebar-menu-action",
    command=@cmd.none,
    variant=Ghost,
    size=IconXs,
    disabled~,
    id~,
    class~,
    title~,
    aria_label~,
    on_click~,
    attrs=Some(element_attrs),
    style=ui_styles(
      [
        SidebarMenuActionStyle,
        SidebarActionSurfaceStyle,
        SidebarActionSizeStyle,
        if show_on_hover {
          SidebarMenuActionHoverStyle
        } else {
          ""
        },
      ],
      style,
    ),
    children,
  )
}

///|
pub fn[C : @html.IsChildren] sidebar_menu_badge(
  id? : String,
  class? : String,
  title? : String,
  attrs? : @html.Attrs,
  style? : Array[String] = [],
  children : C,
) -> @html.Html {
  @html.span(
    style=ui_styles([UiBoxSizing, SidebarMenuBadgeStyle], style),
    id?,
    class?,
    title?,
    attrs=ui_attrs(attrs).data_set("slot", "sidebar-menu-badge"),
    children,
  )
}

///|
pub fn sidebar_menu_skeleton(
  show_icon? : Bool = false,
  id? : String,
  class? : String,
  title? : String,
  attrs? : @html.Attrs,
  style? : Array[String] = [],
) -> @html.Html {
  @html.div(
    style=ui_styles(
      [
        UiBoxSizing,
        "display:flex;height:2rem;align-items:center;gap:0.5rem;padding-inline:0.5rem;border-radius:calc(var(--rui-radius,0.625rem) - 0.125rem)",
      ],
      style,
    ),
    id?,
    class?,
    title?,
    attrs=ui_attrs(attrs)
      .data_set("slot", "sidebar-menu-skeleton")
      .data_set("icon", ui_bool(show_icon))
      .aria_hidden("true"),
    [
      if show_icon {
        skeleton(
          attrs=@html.Attrs::build().data_set("sidebar-skeleton-part", "icon"),
          style=["width:1rem;height:1rem;flex-shrink:0;border-radius:0.25rem"],
        )
      } else {
        @html.nothing
      },
      skeleton(
        attrs=@html.Attrs::build().data_set("sidebar-skeleton-part", "text"),
        style=[
          "display:var(--rui-sidebar-skeleton-text-display,block);height:0.75rem;width:70%;border-radius:0.25rem",
        ],
      ),
    ],
  )
}

///|
pub fn[C : @html.IsChildren] sidebar_menu_sub(
  id? : String,
  class? : String,
  title? : String,
  attrs? : @html.Attrs,
  style? : Array[String] = [],
  children : C,
) -> @html.Html {
  @html.ul(
    style=ui_styles([UiBoxSizing, SidebarMenuSubStyle], style),
    id?,
    class?,
    title?,
    attrs=ui_attrs(attrs).data_set("slot", "sidebar-menu-sub"),
    children,
  )
}

///|
pub fn[C : @html.IsChildren] sidebar_menu_sub_item(
  id? : String,
  class? : String,
  title? : String,
  attrs? : @html.Attrs,
  style? : Array[String] = [],
  children : C,
) -> @html.Html {
  @html.li(
    style=ui_styles([UiBoxSizing, SidebarMenuItemStyle], style),
    id?,
    class?,
    title?,
    attrs=ui_attrs(attrs).data_set("slot", "sidebar-menu-sub-item"),
    children,
  )
}

///|
pub fn[C : @html.IsChildren] sidebar_menu_sub_button(
  href? : String,
  is_active? : Bool = false,
  disabled? : Bool = false,
  aria_label? : String,
  target? : @html.Target = Self,
  rel? : String,
  download? : String,
  on_click? : @cmd.Cmd,
  id? : String,
  class? : String,
  title? : String,
  attrs? : @html.Attrs,
  style? : Array[String] = [],
  children : C,
) -> @html.Html {
  let element_attrs = ui_attrs(attrs)
    .data_set("slot", "sidebar-menu-sub-button")
    .data_set("active", ui_bool(is_active))
    .data_set("state", if is_active { "active" } else { "inactive" })
  if aria_label is Some(label) {
    ignore(element_attrs.aria_label(label))
  }
  if is_active && href is Some(_) {
    ignore(element_attrs.aria_current("page"))
  }
  if disabled {
    ignore(
      element_attrs.aria_disabled("true").data_set("disabled", "").tabindex(-1),
    )
  } else if on_click is Some(command) {
    ignore(element_attrs.on_click(_ => command))
  }
  sidebar_bind_disabled_link(element_attrs, disabled && href is Some(_))
  let styles = ui_styles(
    [
      UiBoxSizing,
      UiFontSans,
      UiTransition,
      SidebarMenuSubButtonStyle,
      if is_active {
        SidebarMenuButtonActiveStyle
      } else {
        ""
      },
      ui_disabled_style(disabled),
    ],
    style,
  )
  match href {
    Some(href) =>
      @html.a(
        style=styles,
        id?,
        class?,
        title?,
        href~,
        target~,
        rel?,
        download?,
        attrs=element_attrs,
        children,
      )
    None =>
      @html.button(
        style=styles,
        id?,
        class?,
        title?,
        type_="button",
        disabled~,
        attrs=element_attrs,
        children,
      )
  }
}

///|
pub fn sidebar_separator(
  id? : String,
  class? : String,
  title? : String,
  attrs? : @html.Attrs,
  style? : Array[String] = [],
) -> @html.Html {
  @html.div(
    style=ui_styles(
      [
        UiBoxSizing,
        "width:auto;height:1px;flex-shrink:0;margin-inline:0.5rem;background:var(--rui-sidebar-border,var(--rui-border,oklch(0.922 0 0)))",
      ],
      style,
    ),
    id?,
    class?,
    title?,
    attrs=ui_attrs(attrs)
      .data_set("slot", "sidebar-separator")
      .role("separator")
      .aria_orientation("horizontal"),
    @html.nothing,
  )
}

///|
pub fn sidebar_input(
  name? : String,
  value? : String,
  placeholder? : String,
  auto_complete? : @html.AutoComplete,
  id? : String,
  class? : String,
  title? : String,
  disabled? : Bool = false,
  aria_label? : String,
  on_change? : @cmd.Emit[String],
  on_input? : @cmd.Emit[String],
  attrs? : @html.Attrs,
  style? : Array[String] = [],
) -> @html.Html {
  let element_attrs = ui_attrs(attrs).data_set("slot", "sidebar-input")
  if aria_label is Some(label) {
    ignore(element_attrs.aria_label(label))
  }
  if disabled {
    ignore(element_attrs.disabled(true).data_set("disabled", ""))
  }
  @html.input(
    input_type=@html.Text,
    name?,
    value?,
    placeholder?,
    auto_complete?,
    style=ui_styles(
      [
        UiBoxSizing,
        UiFontSans,
        UiTextRendering,
        UiTransition,
        InputBaseStyle,
        "height:2rem;--rui-control-base-shadow:0 0 #0000;background:var(--rui-background,oklch(1 0 0));font-size:0.875rem;line-height:1.25rem",
        "display:var(--rui-sidebar-input-display,block)",
        ui_disabled_style(disabled),
      ],
      style,
    ),
    id?,
    class?,
    title?,
    on_change?,
    on_input?,
    attrs=element_attrs,
  )
}

///|
pub fn[C : @html.IsChildren] sidebar_inset(
  id? : String,
  class? : String,
  title? : String,
  attrs? : @html.Attrs,
  style? : Array[String] = [],
  children : C,
) -> @html.Html {
  @html.div(
    style=ui_styles([UiBoxSizing, SidebarInsetStyle], style),
    id?,
    class?,
    title?,
    attrs=ui_attrs(attrs).data_set("slot", "sidebar-inset").role("main"),
    children,
  )
}