# AppShell

- Page: https://jiguang132.com/resources/astryx/docs/app-shell
- Resource: Astryx (https://jiguang132.com/resources/astryx.md)
- All guides: https://jiguang132.com/resources/astryx/docs.md

AppShell is the page shell for an application. It provides slots for top navigation, side navigation, banners, and main content. Use it as the root wrapper for every page. It handles responsive mobile navigation and skip-to-content automatically. Configure side nav collapse on SideNav with its collapsible prop.

## Installation

```bash
npm install @astryxdesign/core@0.7.1 @astryxdesign/theme-neutral@0.7.1 @stylexjs/stylex@0.19.0
```

Requires React 19+, base CSS and a Theme provider. See [Setup](/resources/astryx/docs/getting-started).

## App Shell

AppShell is the page shell for an application. It provides slots for top navigation, side navigation, banners, and main content. Use it as the root wrapper for every page. It handles responsive mobile navigation and skip-to-content automatically. Configure side nav collapse on SideNav with its collapsible prop.

| Prop | Type | Default | Description |
|---|---|---|---|
| children | ReactNode |  | Main content area, rendered inside a <main> element. |
| contentPadding | 0 \| 0.5 \| 1 \| 1.5 \| 2 \| 3 \| 4 \| 5 \| 6 \| 8 \| 10 | 0 | Padding for the main content area. Set based on the dominant content pattern: 4 (16px) for forms/settings/text, 0 for dashboards/maps/tables. Override individual sections with Section. |
| topNav | ReactNode |  | Top navigation slot, typically TopNav. |
| sideNav | ReactNode |  | Side navigation slot, typically SideNav. |
| mobileNav | ReactNode |  | Mobile navigation configuration. Accepts false (disable), a config object (tune auto behavior), or ReactNode (full custom drawer). The config object is {hasToggle?: boolean, isOpen?: boolean, onOpenChange?: (isOpen: boolean) => void, content?: ReactNode, breakpoint?: 'sm' \| 'md' \| 'lg' \| 'xl' \| '2xl' \| 'none', defaultIsMobile?: boolean}; breakpoint defaults to 'md', resolves through the nearest Theme's widthBreakpoints, and switches to the wider layout at equality. 'none' is always non-mobile and ignores defaultIsMobile. |
| banner | ReactNode |  | Banner slot for system-wide announcements, placed above the topNav. |
| height | 'fill' \| 'auto' | 'fill' | Height behavior: 'fill' makes the shell fill the viewport (100dvh) with independent scroll containers; 'auto' lets the shell grow with content and uses sticky positioning for nav. |
| variant | 'wash' \| 'surface' \| 'section' \| 'elevated' | 'elevated' | Navigation background style controlling how nav areas contrast with content. 'wash' uses wash background, 'surface' uses surface background, 'section' adds dividers between nav and content, 'elevated' uses wash nav with elevated surface content and border radius. |
| xstyle | StyleXStyles |  | StyleX styles for layout customization (margins, positioning, sizing). Must be a stylex.create() value, not an inline style object like style={{}}. |

### Usage guidance

- Choose the right height: use "fill" for dashboards with internal scrolling and "auto" for pages that grow with content.
- Set `contentPadding` based on content type: 4 for forms and settings, 0 for tables and dashboards.
- Give every nav slot an accessible name. AppShell renders TopNav and SideNav as separate navigation landmarks, and a screen reader lists them by name, so pass `label` to each one.
- Start the page heading inside `children`. AppShell owns the skip link, the banner landmark and the main landmark, but it renders no heading, so the first heading in the content area is the page h1.
- Avoid: Nest one AppShell inside another; it's the outermost layout frame.
- Avoid: Use for sub-page layouts; use Layout for content areas within AppShell.
- Avoid: Add your own skip link or <main> element. AppShell already renders both, and a second main landmark makes the first ambiguous.

### Theming API

```json
{
  "targets": [
    {
      "className": "astryx-app-shell",
      "visualProps": [
        "variant"
      ]
    },
    {
      "className": "astryx-app-shell-header",
      "visualProps": [
        "variant"
      ]
    },
    {
      "className": "astryx-app-shell-sidenav",
      "visualProps": [
        "variant"
      ]
    }
  ]
}
```

### Anatomy

- **Page shell**: Outermost application frame that owns page-level navigation, responsive shell behavior, and the main content landmark.
- **Skip link**: First focusable element on the page. Visually hidden until focused, then moves focus to the main content area.
- **Banner**: The banner slot, for system-wide announcements. Renders above the top nav, inside the banner landmark.
- **Top navigation**: The topNav slot, typically TopNav. Below the mobile breakpoint it becomes a compact bar carrying the nav toggle.
- **Side navigation**: The sideNav slot, typically SideNav. Inline above the breakpoint, moved into the mobile drawer below it.
- **Main content**: children, rendered in the main landmark. Scrolls internally when height is fill, and with the page when it is auto.
- **Mobile nav drawer**: Generated below the breakpoint from the nav slots unless mobileNav disables or replaces it. A modal dialog: it traps focus and returns focus to the toggle on close.

## useAppShellMobile

Hook for reading and controlling AppShell mobile navigation state from descendants of AppShell. Use it for custom mobile nav triggers, closing the drawer after route changes, or coordinating AppShell-adjacent mobile experiences with the same breakpoint used by mobile nav.

### Returns

```json
[
  {
    "name": "isMobile",
    "type": "boolean",
    "description": "Whether the current viewport is below the AppShell mobile navigation breakpoint. Use this to synchronize AppShell-adjacent mobile UI with the same breakpoint as mobile nav."
  },
  {
    "name": "isMobileNavOpen",
    "type": "boolean",
    "description": "Whether the AppShell-managed mobile navigation drawer is open."
  },
  {
    "name": "mobileNavId",
    "type": "string | undefined",
    "description": "DOM id of the mobile navigation drawer, set by AppShell. Point aria-controls of a custom toggle at this so screen-reader users know which element the toggle expands. Undefined outside an AppShell that manages the drawer."
  },
  {
    "name": "toggleMobileNav",
    "type": "() => void",
    "description": "Toggle the AppShell-managed mobile navigation drawer. No-ops when mobile nav is disabled."
  },
  {
    "name": "openMobileNav",
    "type": "() => void",
    "description": "Open the AppShell-managed mobile navigation drawer. No-ops when mobile nav is disabled."
  },
  {
    "name": "closeMobileNav",
    "type": "() => void",
    "description": "Close the AppShell-managed mobile navigation drawer."
  },
  {
    "name": "isMobileNavEnabled",
    "type": "boolean",
    "description": "Whether AppShell mobile navigation is enabled and managed by AppShell. False when mobileNav is false, there is no nav content, or a fully custom mobileNav ReactNode owns the drawer."
  },
  {
    "name": "hasAutoToggle",
    "type": "boolean",
    "description": "Whether AppShell auto-toggle behavior is enabled. False when mobileNav hasToggle is set to false; combine with isMobile and isMobileNavEnabled before rendering custom toggles."
  }
]
```

### Usage guidance

- Use inside the AppShell tree when building custom mobile navigation controls, route-aware nav items, or UI that should update at the same breakpoint as AppShell mobile nav.
- Prefer MobileNavToggle for the standard hamburger trigger: use this hook when you need custom placement, styling, or extra behavior.
- Call closeMobileNav after a custom mobile nav item changes route so the drawer dismisses cleanly.
- Avoid: Use as a general responsive primitive when the UI is not inside AppShell or does not need to align with AppShell mobile nav: use useMediaQuery instead.
- Avoid: Assume it throws outside AppShell. The hook returns safe defaults and no-op callbacks when no provider is present.


## Examples

- App Shell: A basic app shell with content padding.
- AppShell — Content Only: Minimal shell with no navigation, useful for full-bleed pages, auth screens, or embedded views.
- useAppShellMobile — Custom Mobile Trigger: Custom mobile navigation trigger built with useAppShellMobile. The trigger consumes the surrounding AppShell context instead of rendering its own shell.
- AppShell — Side Nav Only: App shell with SideNav header providing app identity, no TopNav needed.
- AppShell — Top Nav Only: Simple layout with TopNav and no side navigation, suitable for landing pages.
- AppShell — Top Nav with Side Nav: The most common layout with TopNav for app identity and SideNav for page-level navigation.
- AppShell — With Banner: Full layout with TopNav, SideNav, and a dismissable info banner between the nav and content.

## Complete example dependencies

Some examples import optional icons. The per-example install command in the examples list identifies exactly what that example needs. For all examples on this page:

```bash
npm install @heroicons/react@2.2.0
```

---

Index of all JGuang resources: https://jiguang132.com/llms.txt
