Skip to content
← Inventory

MobileNavigationRoot

Full-screen fixed overlay that hosts the mobile navigation; hidden from lg up. Carries modal-dialog semantics: focus is trapped inside the overlay while it is mounted, cycles with Tab, and returns to the trigger once it unmounts. Pressing Escape invokes onClose. The accessible name defaults to "Navigation menu" and can be overridden via aria-label. Renders an m.div, so consumers can drive enter/exit motion (e.g. a slide) by passing initial/animate/exit/transition and wrapping in AnimatePresence. With no motion props it renders statically. Horizontal overflow is clipped (overflow-x-clip) so a nested drill-in view sliding off-screen never spawns a horizontal scrollbar; vertical content still scrolls.

MaterialBank

Usage

import { mobileNavigationAnatomy, MobileNavigationAppHeader, MobileNavigationBrandLockup, MobileNavigationBrandTrigger, MobileNavigationCenteredPanel, MobileNavigationCenteredText, MobileNavigationContent, MobileNavigationDrawerContent, MobileNavigationDrawerProfile, MobileNavigationDrawerProfileProps, MobileNavigationDrawerSection, MobileNavigationDrawerStack, MobileNavigationFrame, MobileNavigationFullWidthAction, MobileNavigationHeader, MobileNavigationHeaderAction, MobileNavigationHeaderSearchSlot, MobileNavigationItem, MobileNavigationItemProps, MobileNavigationList, MobileNavigationProfileAction, MobileNavigationProfileActionProps, MobileNavigationProjectItem, MobileNavigationProjectItemProps, MobileNavigationRoot, MobileNavigationSeparator, MobileNavigationSlottedText } from "@desk/ui/components/mobile-navigation";

Best practices

  • Wire onClose to your open-state setter — the overlay traps focus and fires it on Escape, but you own mounting/unmounting.

  • It's role="dialog"+aria-modal and hidden from lg up, so pair it with a desktop nav rather than showing both at once.

  • Override the default "Navigation menu" accessible name via aria-label when the drawer's purpose is more specific.

Props

MobileNavigationBrandLockup

PropTypeDefaultDescription
alignResponsiveValue<FlexAlign>"stretch"

Cross-axis alignment. Accepts a per-breakpoint responsive object.

as"div" | "li" | "nav" | "ol" | "span" | "ul" | "article" | "aside" | "figcaption" | "figure" | "footer" | "header" | "main" | "section"

Render as a different host element from the closed layout vocabulary ({@link LayoutElement}). The set is deliberately closed: as="button" is a type error, because the design-system answer is the Button component. Ref and event-handler types remain the default element's (HTMLDivElement).

directionResponsiveValue<FlexDirection>"row"

Main-axis direction. Accepts a per-breakpoint responsive object.

gapResponsiveValue<"base" | "none" | "2xs" | "xs" | "sm" | "lg" | "xl" | "2xl" | "3xl" | "3xs">

Gap between items, from the spacing scale. Accepts a per-breakpoint responsive object.

guidelinesbooleanfalse

Show dashed outline guidelines during development.

inlinebooleanfalse

Render as inline-flex instead of flex.

justifyResponsiveValue<FlexJustify>"flex-start"

Main-axis distribution. Accepts a per-breakpoint responsive object.

responsiveModeResponsiveMode"screen"

Whether responsive values resolve against the viewport ("screen") or nearest container ("container").

wrapResponsiveValue<FlexWrap>"nowrap"

Wrap behavior. Accepts a per-breakpoint responsive object.

MobileNavigationBrandTrigger

PropTypeDefaultDescription
asChildboolean | undefinedfalse

Merge props and styling onto the single child element via Slot.

tone"neutral" | "brand" | "danger" | null | undefined
radius"rounded" | "square" | "full" | null | undefined
sizeButtonSizeProp"base"

Size token; icon-* variants produce a square icon-only button. Also accepts a responsive object keyed by viewport breakpoints and/or @-prefixed container queries — e.g. { base: "sm", md: "base", "@sm": "lg" }.

dotstring | undefined

CSS colour for a status dot; empty/undefined renders no dot.

loadingboolean | undefinedfalse

Show a spinner and disable interaction.

squareboolean | null | undefined
emphasis"link" | "outline" | "solid" | "ghost" | "soft" | "transparent" | null | undefined
asInputboolean | null | undefined
align"center" | "end" | "start" | null | undefined
pressedboolean | null | undefined
testIdstring
suffixIconIconComponent | undefined

Trailing icon (Lucide or SVG component) rendered at the end.

loadingLabelstring | undefined

Label shown in place of children while loading.

iconClassNamestring | undefined

Extra classes for the leading icon.

suffixIconClassNamestring | undefined

Extra classes for the trailing icon.

dotPlacement"end" | "start" | undefined"start"

Dot position relative to content.

expandableboolean | undefinedfalse

Icon-only at rest; expands on hover/focus-visible to reveal the label to the right of the icon. Collapsed geometry equals the matching icon-* size, expanded equals the text button of that size — so pass a text size (sm/base/lg) OR its icon twin (icon-sm …); both resolve identically. Requires both icon and children; ignored with asChild or square. The label stays in the DOM in both states (stable accessible name).

focusedboolean | undefined

Force the focus ring on — for documentation/testing only.

hoveredboolean | undefined

Force the hover state on — for documentation/testing only.

MobileNavigationCenteredPanel

PropTypeDefaultDescription
asChildbooleanfalse

Merge props onto the single child element instead of rendering a div.

gapResponsiveValue<"base" | "none" | "2xs" | "xs" | "sm" | "lg" | "xl" | "2xl" | "3xl" | "3xs">"base"

Gap between items, from the spacing scale. Accepts a per-breakpoint responsive object.

alignResponsiveValue<FlexAlign>"stretch"

Cross-axis alignment. Accepts a per-breakpoint responsive object.

justifyResponsiveValue<FlexJustify>"flex-start"

Main-axis distribution. Accepts a per-breakpoint responsive object.

responsiveModeResponsiveMode"screen"

Whether responsive values resolve against the viewport ("screen") or nearest container ("container").

as"div" | "li" | "nav" | "ol" | "span" | "ul" | "article" | "aside" | "figcaption" | "figure" | "footer" | "header" | "main" | "section"

Render as a different host element from the closed layout vocabulary ({@link LayoutElement}). The set is deliberately closed: as="button" is a type error, because the design-system answer is the Button component. Ref and event-handler types remain the default element's (HTMLDivElement). as and asChild are mutually exclusive; if both are passed, asChild wins (a dev-only warning fires).

guidelinesbooleanfalse

Show dashed outline guidelines during development.

MobileNavigationCenteredText

PropTypeDefaultDescription
as"li" | "p" | "span" | "dd" | "dt" | "figcaption" | "legend"

Render as a different host element from the closed text vocabulary ({@link TextElement}). The set is deliberately closed and text-only: as="div" and as="label" are type errors (use Box for decorative wrappers, Label for form labels). Ref and event-handler types remain the default element's (HTMLParagraphElement). as and asChild are mutually exclusive; if both are passed, asChild wins (a dev-only warning fires).

sizeResponsiveValue<"base" | "2xs" | "xs" | "sm" | "lg" | "xl" | "2xl" | "3xl">

Text size — accepts a static size or responsive object like { base: "sm", md: "lg" }

variant"default" | "muted" | "accent"

Text color variant

weight"normal" | "medium" | "semibold"

Font weight

asChildboolean

Merge props onto child element using Slot

MobileNavigationDrawerContent

PropTypeDefaultDescription
showHandlebooleantrue

Show the drag handle (bottom drawers).

MobileNavigationDrawerProfile

PropTypeDefaultDescription
displayName(required)string
email(required)string | undefined

Secondary line under the name; hidden when absent.

userAvatarName(required)string | undefined

Name used to derive the avatar initials fallback.

userAvatarUrl(required)string | null | undefined

Avatar image URL; falls back to initials when absent.

MobileNavigationDrawerSection

PropTypeDefaultDescription
asChildbooleanfalse

Merge props onto the single child element instead of rendering a div.

gapResponsiveValue<"base" | "none" | "2xs" | "xs" | "sm" | "lg" | "xl" | "2xl" | "3xl" | "3xs">"base"

Gap between items, from the spacing scale. Accepts a per-breakpoint responsive object.

alignResponsiveValue<FlexAlign>"stretch"

Cross-axis alignment. Accepts a per-breakpoint responsive object.

justifyResponsiveValue<FlexJustify>"flex-start"

Main-axis distribution. Accepts a per-breakpoint responsive object.

responsiveModeResponsiveMode"screen"

Whether responsive values resolve against the viewport ("screen") or nearest container ("container").

as"div" | "li" | "nav" | "ol" | "span" | "ul" | "article" | "aside" | "figcaption" | "figure" | "footer" | "header" | "main" | "section"

Render as a different host element from the closed layout vocabulary ({@link LayoutElement}). The set is deliberately closed: as="button" is a type error, because the design-system answer is the Button component. Ref and event-handler types remain the default element's (HTMLDivElement). as and asChild are mutually exclusive; if both are passed, asChild wins (a dev-only warning fires).

guidelinesbooleanfalse

Show dashed outline guidelines during development.

MobileNavigationDrawerStack

PropTypeDefaultDescription
asChildbooleanfalse

Merge props onto the single child element instead of rendering a div.

gapResponsiveValue<"base" | "none" | "2xs" | "xs" | "sm" | "lg" | "xl" | "2xl" | "3xl" | "3xs">"base"

Gap between items, from the spacing scale. Accepts a per-breakpoint responsive object.

alignResponsiveValue<FlexAlign>"stretch"

Cross-axis alignment. Accepts a per-breakpoint responsive object.

justifyResponsiveValue<FlexJustify>"flex-start"

Main-axis distribution. Accepts a per-breakpoint responsive object.

responsiveModeResponsiveMode"screen"

Whether responsive values resolve against the viewport ("screen") or nearest container ("container").

as"div" | "li" | "nav" | "ol" | "span" | "ul" | "article" | "aside" | "figcaption" | "figure" | "footer" | "header" | "main" | "section"

Render as a different host element from the closed layout vocabulary ({@link LayoutElement}). The set is deliberately closed: as="button" is a type error, because the design-system answer is the Button component. Ref and event-handler types remain the default element's (HTMLDivElement). as and asChild are mutually exclusive; if both are passed, asChild wins (a dev-only warning fires).

guidelinesbooleanfalse

Show dashed outline guidelines during development.

MobileNavigationFrame

PropTypeDefaultDescription
asChildbooleanfalse

Merge props onto the single child element instead of rendering a div.

gapResponsiveValue<"base" | "none" | "2xs" | "xs" | "sm" | "lg" | "xl" | "2xl" | "3xl" | "3xs">"base"

Gap between items, from the spacing scale. Accepts a per-breakpoint responsive object.

alignResponsiveValue<FlexAlign>"stretch"

Cross-axis alignment. Accepts a per-breakpoint responsive object.

justifyResponsiveValue<FlexJustify>"flex-start"

Main-axis distribution. Accepts a per-breakpoint responsive object.

responsiveModeResponsiveMode"screen"

Whether responsive values resolve against the viewport ("screen") or nearest container ("container").

as"div" | "li" | "nav" | "ol" | "span" | "ul" | "article" | "aside" | "figcaption" | "figure" | "footer" | "header" | "main" | "section"

Render as a different host element from the closed layout vocabulary ({@link LayoutElement}). The set is deliberately closed: as="button" is a type error, because the design-system answer is the Button component. Ref and event-handler types remain the default element's (HTMLDivElement). as and asChild are mutually exclusive; if both are passed, asChild wins (a dev-only warning fires).

guidelinesbooleanfalse

Show dashed outline guidelines during development.

MobileNavigationFullWidthAction

PropTypeDefaultDescription
tone"neutral" | "brand" | "danger" | null | undefined
radius"rounded" | "square" | "full" | null | undefined
squareboolean | null | undefined
emphasis"link" | "outline" | "solid" | "ghost" | "soft" | "transparent" | null | undefined
asInputboolean | null | undefined
align"center" | "end" | "start" | null | undefined
pressedboolean | null | undefined
sizeButtonSizeProp"base"

Size token; icon-* variants produce a square icon-only button. Also accepts a responsive object keyed by viewport breakpoints and/or @-prefixed container queries — e.g. { base: "sm", md: "base", "@sm": "lg" }.

asChildboolean | undefinedfalse

Merge props and styling onto the single child element via Slot.

loadingboolean | undefinedfalse

Show a spinner and disable interaction.

loadingLabelstring | undefined

Label shown in place of children while loading.

iconIconComponent | undefined

Leading icon (Lucide or SVG component); with no children it becomes an icon-only button.

iconClassNamestring | undefined

Extra classes for the leading icon.

suffixIconIconComponent | undefined

Trailing icon (Lucide or SVG component) rendered at the end.

suffixIconClassNamestring | undefined

Extra classes for the trailing icon.

dotstring | undefined

CSS colour for a status dot; empty/undefined renders no dot.

dotPlacement"end" | "start" | undefined"start"

Dot position relative to content.

expandableboolean | undefinedfalse

Icon-only at rest; expands on hover/focus-visible to reveal the label to the right of the icon. Collapsed geometry equals the matching icon-* size, expanded equals the text button of that size — so pass a text size (sm/base/lg) OR its icon twin (icon-sm …); both resolve identically. Requires both icon and children; ignored with asChild or square. The label stays in the DOM in both states (stable accessible name).

focusedboolean | undefined

Force the focus ring on — for documentation/testing only.

hoveredboolean | undefined

Force the hover state on — for documentation/testing only.

testIdstring

MobileNavigationHeader

PropTypeDefaultDescription
directionResponsiveValue<FlexDirection>"row"

Main-axis direction. Accepts a per-breakpoint responsive object.

gapResponsiveValue<"base" | "none" | "2xs" | "xs" | "sm" | "lg" | "xl" | "2xl" | "3xl" | "3xs">

Gap between items, from the spacing scale. Accepts a per-breakpoint responsive object.

inlinebooleanfalse

Render as inline-flex instead of flex.

wrapResponsiveValue<FlexWrap>"nowrap"

Wrap behavior. Accepts a per-breakpoint responsive object.

alignResponsiveValue<FlexAlign>"stretch"

Cross-axis alignment. Accepts a per-breakpoint responsive object.

justifyResponsiveValue<FlexJustify>"flex-start"

Main-axis distribution. Accepts a per-breakpoint responsive object.

responsiveModeResponsiveMode"screen"

Whether responsive values resolve against the viewport ("screen") or nearest container ("container").

as"div" | "li" | "nav" | "ol" | "span" | "ul" | "article" | "aside" | "figcaption" | "figure" | "footer" | "header" | "main" | "section"

Render as a different host element from the closed layout vocabulary ({@link LayoutElement}). The set is deliberately closed: as="button" is a type error, because the design-system answer is the Button component. Ref and event-handler types remain the default element's (HTMLDivElement).

guidelinesbooleanfalse

Show dashed outline guidelines during development.

onClose(required)() => void

MobileNavigationHeaderAction

PropTypeDefaultDescription
asChildboolean | undefinedfalse

Merge props and styling onto the single child element via Slot.

tone"neutral" | "brand" | "danger" | null | undefined
radius"rounded" | "square" | "full" | null | undefined
sizeButtonSizeProp"base"

Size token; icon-* variants produce a square icon-only button. Also accepts a responsive object keyed by viewport breakpoints and/or @-prefixed container queries — e.g. { base: "sm", md: "base", "@sm": "lg" }.

dotstring | undefined

CSS colour for a status dot; empty/undefined renders no dot.

loadingboolean | undefinedfalse

Show a spinner and disable interaction.

squareboolean | null | undefined
iconIconComponent | undefined

Leading icon (Lucide or SVG component); with no children it becomes an icon-only button.

emphasis"link" | "outline" | "solid" | "ghost" | "soft" | "transparent" | null | undefined
asInputboolean | null | undefined
align"center" | "end" | "start" | null | undefined
pressedboolean | null | undefined
testIdstring
suffixIconIconComponent | undefined

Trailing icon (Lucide or SVG component) rendered at the end.

loadingLabelstring | undefined

Label shown in place of children while loading.

iconClassNamestring | undefined

Extra classes for the leading icon.

suffixIconClassNamestring | undefined

Extra classes for the trailing icon.

dotPlacement"end" | "start" | undefined"start"

Dot position relative to content.

expandableboolean | undefinedfalse

Icon-only at rest; expands on hover/focus-visible to reveal the label to the right of the icon. Collapsed geometry equals the matching icon-* size, expanded equals the text button of that size — so pass a text size (sm/base/lg) OR its icon twin (icon-sm …); both resolve identically. Requires both icon and children; ignored with asChild or square. The label stays in the DOM in both states (stable accessible name).

focusedboolean | undefined

Force the focus ring on — for documentation/testing only.

hoveredboolean | undefined

Force the hover state on — for documentation/testing only.

MobileNavigationItem

PropTypeDefaultDescription
activeboolean

Mark the row as the current location (adds a filled background).

asChildbooleanfalse

Render as the single child element (e.g. a link) instead of a button.

childrenReact.ReactNode
destructiveboolean

Render the label in the destructive color.

dotboolean | "neutral" | "brand" | "danger" | "warning" | "success" | "info"

Show a status dot in place of the leading icon. true uses the default affirmative tint; a {@link Tone} value tints it from the shared dot palette.

iconimport("../../tokens/icon-component").IconComponent

Leading icon; omit to reserve leading space with no glyph.

label(required)React.ReactNode

Row text (required).

suffixIconimport("../../tokens/icon-component").IconComponent

Trailing icon shown after the label.

tone"neutral" | "brand" | "danger" | null | undefined
radius"rounded" | "square" | "full" | null | undefined
sizeButtonSizeProp"base"

Size token; icon-* variants produce a square icon-only button. Also accepts a responsive object keyed by viewport breakpoints and/or @-prefixed container queries — e.g. { base: "sm", md: "base", "@sm": "lg" }.

loadingboolean | undefinedfalse

Show a spinner and disable interaction.

squareboolean | null | undefined
emphasis"link" | "outline" | "solid" | "ghost" | "soft" | "transparent" | null | undefined
asInputboolean | null | undefined
align"center" | "end" | "start" | null | undefined
pressedboolean | null | undefined
testIdstring
loadingLabelstring | undefined

Label shown in place of children while loading.

iconClassNamestring | undefined

Extra classes for the leading icon.

suffixIconClassNamestring | undefined

Extra classes for the trailing icon.

dotPlacement"end" | "start" | undefined"start"

Dot position relative to content.

expandableboolean | undefinedfalse

Icon-only at rest; expands on hover/focus-visible to reveal the label to the right of the icon. Collapsed geometry equals the matching icon-* size, expanded equals the text button of that size — so pass a text size (sm/base/lg) OR its icon twin (icon-sm …); both resolve identically. Requires both icon and children; ignored with asChild or square. The label stays in the DOM in both states (stable accessible name).

focusedboolean | undefined

Force the focus ring on — for documentation/testing only.

hoveredboolean | undefined

Force the hover state on — for documentation/testing only.

MobileNavigationList

PropTypeDefaultDescription
asChildbooleanfalse

Merge props onto the single child element instead of rendering a div.

gapResponsiveValue<"base" | "none" | "2xs" | "xs" | "sm" | "lg" | "xl" | "2xl" | "3xl" | "3xs">"base"

Gap between items, from the spacing scale. Accepts a per-breakpoint responsive object.

alignResponsiveValue<FlexAlign>"stretch"

Cross-axis alignment. Accepts a per-breakpoint responsive object.

justifyResponsiveValue<FlexJustify>"flex-start"

Main-axis distribution. Accepts a per-breakpoint responsive object.

responsiveModeResponsiveMode"screen"

Whether responsive values resolve against the viewport ("screen") or nearest container ("container").

as"div" | "li" | "nav" | "ol" | "span" | "ul" | "article" | "aside" | "figcaption" | "figure" | "footer" | "header" | "main" | "section"

Render as a different host element from the closed layout vocabulary ({@link LayoutElement}). The set is deliberately closed: as="button" is a type error, because the design-system answer is the Button component. Ref and event-handler types remain the default element's (HTMLDivElement). as and asChild are mutually exclusive; if both are passed, asChild wins (a dev-only warning fires).

guidelinesbooleanfalse

Show dashed outline guidelines during development.

MobileNavigationProfileAction

PropTypeDefaultDescription
avatarName(required)string | undefined

Name used to derive the avatar initials fallback.

displayName(required)string

User's display name shown as the button label.

loadingbooleanfalse

Show a loading label and disable the button.

userAvatarUrl(required)string | null | undefined

Avatar image URL; falls back to initials when absent.

asChildboolean | undefinedfalse

Merge props and styling onto the single child element via Slot.

tone"neutral" | "brand" | "danger" | null | undefined
radius"rounded" | "square" | "full" | null | undefined
sizeButtonSizeProp"base"

Size token; icon-* variants produce a square icon-only button. Also accepts a responsive object keyed by viewport breakpoints and/or @-prefixed container queries — e.g. { base: "sm", md: "base", "@sm": "lg" }.

dotstring | undefined

CSS colour for a status dot; empty/undefined renders no dot.

squareboolean | null | undefined
iconIconComponent | undefined

Leading icon (Lucide or SVG component); with no children it becomes an icon-only button.

emphasis"link" | "outline" | "solid" | "ghost" | "soft" | "transparent" | null | undefined
asInputboolean | null | undefined
align"center" | "end" | "start" | null | undefined
pressedboolean | null | undefined
testIdstring
suffixIconIconComponent | undefined

Trailing icon (Lucide or SVG component) rendered at the end.

loadingLabelstring | undefined

Label shown in place of children while loading.

iconClassNamestring | undefined

Extra classes for the leading icon.

suffixIconClassNamestring | undefined

Extra classes for the trailing icon.

dotPlacement"end" | "start" | undefined"start"

Dot position relative to content.

expandableboolean | undefinedfalse

Icon-only at rest; expands on hover/focus-visible to reveal the label to the right of the icon. Collapsed geometry equals the matching icon-* size, expanded equals the text button of that size — so pass a text size (sm/base/lg) OR its icon twin (icon-sm …); both resolve identically. Requires both icon and children; ignored with asChild or square. The label stays in the DOM in both states (stable accessible name).

focusedboolean | undefined

Force the focus ring on — for documentation/testing only.

hoveredboolean | undefined

Force the hover state on — for documentation/testing only.

MobileNavigationProjectItem

PropTypeDefaultDescription
activeboolean

Mark the project as current (dot indicator + filled background).

archivedboolean

Dim the label to indicate an archived project.

children(required)React.ReactNode
label(required)React.ReactNode
onAction(required)() => void

Invoked when the trailing action button is pressed.

actionIcon(required)import("../../tokens/icon-component").IconComponent
actionLabel(required)string

Accessible label for the trailing action button.

MobileNavigationRoot

PropTypeDefaultDescription
onClose() => void

MobileNavigationSeparator

PropTypeDefaultDescription
variant"strong" | "default" | "muted" | "accent""default"

Visual weight of the separator line.

Also exports

  • constmobileNavigationAnatomy
  • componentMobileNavigationAppHeader
  • componentMobileNavigationContent
  • interfaceMobileNavigationDrawerProfileProps
  • componentMobileNavigationHeaderSearchSlot
  • interfaceMobileNavigationItemProps
  • interfaceMobileNavigationProfileActionProps
  • interfaceMobileNavigationProjectItemProps
  • componentMobileNavigationSlottedText

Anatomy

MobileNavigationRoot

mobile-navigation-root

Root controlling open state for the mobile drawer

MobileNavigationFrame

mobile-navigation-frame

Full-height frame that stacks header and content

MobileNavigationHeader

mobile-navigation-header

Branded top bar with a close action

MobileNavigationContent

mobile-navigation-content

Scrollable region holding the navigation list

MobileNavigationItem

mobile-navigation-item

Single navigation entry with icon and active state