> ## Documentation Index
> Fetch the complete documentation index at: https://anvil.servicetitan.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Page – Code

> The foundation for creating page layouts in Anvil2.

export const LiveCode = ({children, customHeight, clickToLoad, example, fullWidth, fullHeight, hideCodeInLiveCode, screenshot, screenshotOnly, showCode: showCodeProp}) => {
  const SCREENSHOTS_BASE = "https://servicetitan.github.io/anvil2-docs-live-code/screenshots";
  const STACKBLITZ_BASE = "https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples";
  const [showCodeBlock, setShowCodeBlock] = useState(showCodeProp ?? false);
  const [isLocalOverride, setIsLocalOverride] = useState(false);
  useEffect(() => {
    const examplePath = `/images/live-code-screenshots-tmp/${example}.png`;
    fetch(examplePath, {
      method: "HEAD"
    }).then(r => {
      if (r.ok) setIsLocalOverride(true);
    }).catch(() => {});
  }, [example]);
  const screenshotBase = isLocalOverride ? "/images/live-code-screenshots-tmp" : SCREENSHOTS_BASE;
  if (screenshotOnly) {
    return <Frame className="flex flex-col">
        <div className="flex dark:hidden" style={{
      justifyContent: "center",
      alignItems: "center",
      width: fullWidth ? "100%" : "50%",
      minHeight: fullHeight ? "284px" : undefined,
      background: "#FFFFFF"
    }}>
          <img srcset={`${screenshotBase}/${example}.png, ${screenshotBase}/${example}-2x.png 2x`} src={`${screenshotBase}/${example}.png`} alt={example} noZoom />
        </div>
        <div className="hidden dark:flex" style={{
      justifyContent: "center",
      alignItems: "center",
      width: fullWidth ? "100%" : "50%",
      minHeight: fullHeight ? "284px" : undefined,
      background: "#141414"
    }}>
          <img srcset={`${screenshotBase}/${example}-dark.png, ${screenshotBase}/${example}-dark-2x.png 2x`} src={`${screenshotBase}/${example}-dark.png`} alt={example} noZoom />
        </div>
      </Frame>;
  }
  if (screenshot) {
    return <Frame className="flex flex-col -mb-2">
        <div className="flex dark:hidden bg-white dark:bg-codeblock border border-gray-950/10 dark:border-white/10 dark:twoslash-dark rounded-2xl overflow-hidden" style={{
      justifyContent: "center",
      alignItems: "center",
      width: fullWidth ? "100%" : "50%",
      minHeight: fullHeight ? "284px" : undefined
    }}>
          <img srcset={`${screenshotBase}/${example}.png, ${screenshotBase}/${example}-2x.png 2x`} src={`${screenshotBase}/${example}.png`} alt={example} noZoom />
        </div>

        <div className="hidden dark:flex bg-white dark:bg-codeblock border border-gray-950/10 dark:border-white/10 dark:twoslash-dark rounded-2xl overflow-hidden" style={{
      background: "#141414",
      justifyContent: "center",
      alignItems: "center",
      width: fullWidth ? "100%" : "50%",
      minHeight: fullHeight ? "284px" : undefined
    }}>
          <img srcset={`${screenshotBase}/${example}-dark.png, ${screenshotBase}/${example}-dark-2x.png 2x`} src={`${screenshotBase}/${example}-dark.png`} alt={example} noZoom />
        </div>

        <div className="flex justify-end items-center text-xs py-2 px-1 gap-4">
          {!showCodeProp ? <button className="inline-flex justify-end items-center text-gray-700 dark:text-gray-50 hover:text-blue-500 dark:hover:text-blue-300 transition-colors group self-end gap-1 cursor-pointer" onClick={() => setShowCodeBlock(!showCodeBlock)} style={{
      appearance: "none"
    }}>
              <Icon icon="code" size="12px" className="group-hover:bg-blue-500 dark:group-hover:bg-blue-300" />
              <span>{showCodeBlock ? "Hide code" : "Show code"}</span>
            </button> : null}

          <a className="inline-flex justify-end items-center hover:text-blue-500 dark:hover:text-blue-300 transition-colors group self-end gap-1" href={`${STACKBLITZ_BASE}/${example}?file=src/App.tsx`} target="_blank" rel="noreferrer">
            <Icon icon="bolt" size="12px" className="group-hover:bg-blue-500 dark:group-hover:bg-blue-300" />
            <span>StackBlitz demo</span>
          </a>
        </div>

        <div className="grid transition-[grid-template-rows] duration-300 ease-in-out overflow-auto overflow-y-hidden overflow-x-auto" style={showCodeBlock ? {
      gridTemplateRows: "1fr"
    } : {
      gridTemplateRows: "0fr"
    }}>
          <div style={{
      minHeight: 0,
      overflowX: "auto",
      overflowY: "hidden",
      marginBlockStart: "-1.25rem",
      marginBlockEnd: "-1.5rem"
    }}>
            {children}
          </div>
        </div>
      </Frame>;
  } else {
    return <div style={{
      display: "flex",
      width: fullWidth ? "100%" : "50%",
      minHeight: customHeight ? customHeight : "316px",
      resize: "vertical",
      overflow: "auto"
    }}>
        <iframe title={example} style={{
      flex: 1,
      width: fullWidth ? "100%" : "50%",
      minHeight: customHeight ? customHeight : "316px"
    }} src={`${STACKBLITZ_BASE}/${example}?embed=1&hideNavigation=1&hideExplorer=1&terminalHeight=0&file=src/App.tsx${clickToLoad ? "&ctl=1" : ""}${hideCodeInLiveCode ? "&view=preview" : ""}`} allow="accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking" sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts" />
      </div>;
  }
};

<Tabs>
  <Tab title="Implementation">
    <LiveCode showCode example="page-playground-full-page" fullWidth screenshot>
      ```tsx lines expandable theme={null}
      import {
        Page,
        Text,
        SideNav,
        Link,
        Layout,
        Flex,
        Button,
      } from "@servicetitan/anvil2";
      import { useState } from "react";

      function App() {
        const [panelOpen, setPanelOpen] = useState(false);

        const breadcrumbs = [
          { href: "/", children: "Home" },
          { href: "/section", children: "Section" },
          { children: "Current Page" },
        ];

        const chips = [{ label: "Status" }, { label: "Draft" }];

        const actions = {
          primary: {
            label: "Primary Action",
            onClick: () => console.log("Primary clicked"),
          },
          secondary: [
            {
              label: "Secondary Action",
              onClick: () => console.log("Secondary clicked"),
            },
          ],
        };

        return (
          <div style={{ width: "100vw" }}>
            <Page>
              <Page.Sidebar>
                <Page.SidebarHeader>
                  <Text variant="headline" el="h2">
                    Sidebar Title
                  </Text>
                </Page.SidebarHeader>
                <SideNav>
                  <SideNav.Link id="01" href="#" active>
                    First Link
                  </SideNav.Link>
                  <SideNav.Link id="02" href="#">
                    Second Link
                  </SideNav.Link>
                  <SideNav.Link id="03" href="#">
                    Third Link
                  </SideNav.Link>
                </SideNav>
              </Page.Sidebar>

              <Page.Header
                title="Page Header"
                breadcrumbs={breadcrumbs}
                chips={chips}
                actions={actions}
                preferenceAction={{
                  "aria-label": "Open settings",
                  onClick: () => console.log("Settings clicked"),
                }}
                description={
                  <Text>
                    This demonstrates all header elements with{" "}
                    <Text inline>
                      <Link href="#" appearance="primary">
                        standard actions
                      </Link>
                    </Text>
                  </Text>
                }
              />

              <Page.Content style={{ height: "15rem" }}>
                <Layout>
                  <Layout.Item span={12}>
                    <Flex direction="column" gap="2">
                      <Text variant="headline" el="h1" size="large">
                        Page Content
                      </Text>
                      <Text>
                        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed
                        do eiusmod tempor incididunt ut labore et dolore magna aliqua.
                      </Text>
                      <Button onClick={() => setPanelOpen((p) => !p)}>
                        Toggle Panel Open
                      </Button>
                    </Flex>
                  </Layout.Item>
                </Layout>
              </Page.Content>
              <Page.Panel open={panelOpen}>
                <Flex direction="column" gap="2">
                  <Text variant="headline" el="h2">
                    Page Panel
                  </Text>
                  <Text>Lorem ipsum dolor sit amet.</Text>
                </Flex>
              </Page.Panel>
              <Page.Footer>
                <Text>Page Footer</Text>
              </Page.Footer>
            </Page>
          </div>
        );
      }

      export default App;
      ```
    </LiveCode>

    ## Common Examples

    ```tsx theme={null}
    import { Page, SideNav, Text } from "@servicetitan/anvil2";

    const ExampleComponent = () => (
      <Page>
        <Page.Sidebar>
          <Page.SidebarHeader>
            <Text variant="headline" el="h2">
              Sidebar Header
            </Text>
          </Page.SidebarHeader>
          <SideNav>
            <SideNav.Link id="01" href="#">
              Nav Link
            </SideNav.Link>
          </SideNav>
        </Page.Sidebar>

        <Page.Header title="Page Title" />

        <Page.Content>
          <Text>Page content.</Text>
        </Page.Content>
      </Page>
    );
    ```

    ### Page components

    In Anvil2, the `Page` component is used to create full-page layouts, along with a few sub-components listed below. When implementing Page, ensure you are using the [method](https://docs.st.dev/docs/frontend/monolith-layout/#anvil2-layout) defined by ST Platform.

    * `Page`: the parent component to configure full-page layouts.
    * `Page.Sidebar`: a collapsible section on the left side of the page. Below 1024px it becomes a popover opened from a trigger button.
    * `Page.SidebarHeader`: used as the first child of `Page.Sidebar` to format a header using a [Text](/docs/web/components/text/code/) component. The heading is hidden from assistive technology while the sidebar is collapsed.
    * `Page.Header`: top section of the page where page actions and title go. The title renders as an `h1`.
    * `Page.Content`: wraps the primary content of the page, and renders as an HTML `section` element. Inside `Page` it portals into the shell's `main` landmark. Outside `Page` it renders in place. Use it with a [`Layout`](/docs/web/components/layout/design) component so the content width matches `Page.Header`.
    * `Page.Panel`: an optional section on the right side of the page. The root is a `div`. Omit `open` for a persistent panel. Pass `open` to show and hide it. Below 1024px, a controlled panel renders inside a [Dialog](/docs/web/components/dialog/code).
    * `Page.Panel.Header`, `Page.Panel.Content`, and `Page.Panel.Footer`: panel regions on the beta `Page` imported from `@servicetitan/anvil2/beta`. The header stays pinned. Content and footer can pin with `sticky`.
    * `Page.Footer`: an optional footer inside the page. It renders an HTML `footer`, portals into the shell's footer slot, and supports [layout utility props](/docs/web/utilities/layout-props). Outside `Page` it renders in place.

    ### Adding side bars to the page

    The `Page.Sidebar` component is usually the first child of a `Page`, and adds a collapsible section to the left of the rest of the page content.

    The `Page.Sidebar` should generally have a `Page.SidebarHeader` and `SideNav` as its `children` to create a consistent, responsive, and user-friendly in-page navigation experience.

    The sidebar can be omitted if the current page does not require a secondary navigation.

    ```tsx theme={null}
    <Page>
      <Page.Sidebar>
        <Page.SidebarHeader>
          <Text variant="headline" el="h2">
            Sidebar Header
          </Text>
        </Page.SidebarHeader>
        <SideNav>{/* ... side nav links */}</SideNav>
      </Page.Sidebar>
      <Page.Content>{/* ... page content */}</Page.Content>
    </Page>
    ```

    #### Sidebar collapsed state in local storage

    In order to maintain the collapsed state of the page sidebar after refreshing or navigating to another page, a local storage value is saved to the `"sidebar-collapsed"` key. The key name can be overridden using the `localStorageKey` prop.

    ### Adding the header to the page

    The `Page.Header` component should go right above the `Page.Content` section to ensure proper placement. It houses the page title, breadcrumb navigation, page classifications via chips, page actions, page description, and the page preference/setting action trigger.

    The `Page.Header` is responsive in nature and has the `Layout` component built into it. It accepts the same properties you would set on your `Page.Content` layout via its own `layout` property, and it is recommended that you keep these values in sync to ensure constraints for the sections remain the same. Both default to a fixed layout with a `1280px` max width; omit `layout` unless the content `Layout` is `fluid` or uses a non-default `variant`.

    The title in the `Page.Header` will always be an `h1` to adhere to SEO and accessibility best practices.

    While the page description is a `ReactNode`, it is recommended to keep the descriptions simple by using `Text` and `Link` components. If a page action needs to be made for the page, use the actions property and not the description area.

    ```tsx theme={null}
    <Page>
      <Page.Sidebar>{/* ... sidebar content */}</Page.Sidebar>

      <Page.Header
        title="Title"
        breadcrumbs={breadcrumbs}
        chips={chips}
        actions={actions}
        preferenceAction={{
          "aria-label": "Open settings",
          onClick: () => {},
        }}
        description={
          <Text>
            This page demonstrates all header elements with
            <Text inline>
              <Link href="#" appearance="primary">
                standard actions
              </Link>
            </Text>
          </Text>
        }
      />

      <Page.Content>{/* ... page content */}</Page.Content>
    </Page>
    ```

    #### User-editing for the header title text

    To enable editing the page title, assign an object with an `onChange` handler to the `title` prop. This handler will be passed the updated title as a string, which can be used to manage the state for the title.

    For accessibility, it is recommended to announce that the editing of the page title was successful, which can be accomplished by using a `toast`.

    ```tsx theme={null}
    <Page>
      <Page.Header
        title={{
          text: title,
          onChange: (newTitle) => {
            setTitle(newTitle);
            toast.success({ title: "Page title edited" });
          }
        }}
      />
    </Page>
    ```

    ### Adding panels to the page

    Place `Page.Panel` as a child of `Page`, usually after the sidebar and content.

    Omit `open` when the panel is always present. Pass `open` when the page shows and hides it. Below 1024px, a panel with `open` set renders inside a dialog instead of beside the content. A panel that omits `open` stays in the page flow and stacks under the main content at that width.

    `size` sets the desktop width: `"small"` (16rem), `"medium"` (24rem, the default), or `"large"` (30rem). Panels pad their inner content. Set `noPadding` to remove that padding.

    ```tsx theme={null}
    <Page>
      <Page.Sidebar>{/* ... sidebar content  */}</Page.Sidebar>
      <Page.Content>{/* ... page content */}</Page.Content>
      <Page.Panel open size="large">
        {/* ... panel content */}
      </Page.Panel>
    </Page>
    ```

    #### Drill-down inside a panel

    `Page.Panel` hosts [DrillDown](/docs/web/components/drilldown/code) the same way `Dialog` and `Drawer` do. The stack starts closed. `defaultDrillDownIndex` is the screen the first `next()` opens from that closed stack, and the index restored when the panel closes. It defaults to `0`. When a parent needs `setIndex`, `back`, or `next`, spread `drillDownProps` from `useDrillDown()` onto the panel.

    ```tsx theme={null}
    import { Button, DrillDown, Page, useDrillDown } from "@servicetitan/anvil2";

    function Example() {
      const { drillDownProps, next } = useDrillDown();

      return (
        <Page>
          <Page.Content>
            <Button onClick={() => next()}>Next</Button>
          </Page.Content>
          <Page.Panel open {...drillDownProps}>
            <DrillDown index={0}>
              <DrillDown.Content>First screen</DrillDown.Content>
            </DrillDown>
          </Page.Panel>
        </Page>
      );
    }
    ```

    #### Panel regions on the beta Page

    The beta `Page` from `@servicetitan/anvil2/beta` adds `Page.Panel.Header`, `Page.Panel.Content`, and `Page.Panel.Footer`. The header is pinned above the scrolling regions. Set `sticky` on a content region to pin it under the header, and on the footer to pin it to the bottom of the panel. Footer actions wrap and align to the end.

    ```tsx theme={null}
    import { Button, Flex, Text } from "@servicetitan/anvil2";
    import { Page } from "@servicetitan/anvil2/beta";

    function Example() {
      return (
        <Page>
          <Page.Header title="Records" />
          <Page.Content>{/* ... page content */}</Page.Content>
          <Page.Panel open>
            <Page.Panel.Header>
              <Text variant="headline" el="h2">
                Details
              </Text>
            </Page.Panel.Header>
            <Page.Panel.Content sticky>
              <Text>Filters stay visible while the list scrolls.</Text>
            </Page.Panel.Content>
            <Page.Panel.Content>{/* ... long content */}</Page.Panel.Content>
            <Page.Panel.Footer sticky>
              <Flex gap="3">
                <Button>Cancel</Button>
                <Button appearance="primary">Save</Button>
              </Flex>
            </Page.Panel.Footer>
          </Page.Panel>
        </Page>
      );
    }
    ```

    ### Scrolling

    `Page` from `@servicetitan/anvil2` scrolls with the window. At 1024px and wider, a virtual scrollbar on the content stays in sync with that window scroll.

    Beta `Page` from `@servicetitan/anvil2/beta` uses one scroll region for every `Page.Content` at 1024px and wider. Those sections portal into a single `main` landmark, and that landmark scrolls on its own. It is a tab stop: focus it, then use Page Up and Page Down. Below 1024px, the beta page scrolls with the document and the landmark is not a tab stop.

    ### Adding a footer to the page

    The `Page.Footer` component renders a semantic `footer` element within the page content area. It can be placed as a child of `Page` after the content or panel to add supplemental information or secondary actions.

    ```tsx theme={null}
    <Page>
      <Page.Content>{/* ... page content */}</Page.Content>
      <Page.Footer>
        {/* ... footer content */}
      </Page.Footer>
    </Page>
    ```

    ## Best Practices

    ### Use with Layout, Grid, and Flex components

    The `Page` component is the first step in creating consistent and responsive page layouts using Anvil2. Three other components are available to configure dynamic layouts on a page:

    * [Layout](/docs/web/components/layout/code): define content max-width and column structure (usually the direct descendant of `Page.Content`) that matches `Page.Header`.
    * [Grid](/docs/web/components/grid/code): easily use [CSS Grid](https://css-tricks.com/snippets/css/complete-guide-grid/) to create dynamic content areas.
    * [Flex](/docs/web/components/flex/code): create [CSS Flexbox](https://css-tricks.com/snippets/css/a-guide-to-flexbox/) containers for simple to moderate content layout.

    ### Handling horizontal scrolling

    The Page component does not allow horizontal scrolling at the page level by default. This prevents usability and accessibility issues, as users expect vertical scrolling and horizontal scrolling disrupts this mental model.

    #### Migrating legacy pages

    When migrating legacy pages that aren't responsive to the A2 Page layout, the recommended approach is to make the page responsive so it works well on all screen sizes. However, if making the page fully responsive is not possible, you can add horizontal scroll handling at the component level for wide content. Wrap specific components that need more horizontal space in a container with `overflow: auto`:

    ```tsx theme={null}
    <div style={{ overflowX: "auto" }}>
      {/* Wide content that needs horizontal scrolling */}
    </div>
    ```

    This approach ensures that page headers, body text, and navigation remain accessible without horizontal scrolling, while allowing specific components to scroll independently when needed. Some components like [Data Table](/docs/web/components/data-table/code) have horizontal scrolling built in.

    ## React Accessibility

    `Page.Header` renders the title as an `h1`. Use one header per page.

    The desktop sidebar toggle is a button named "Open Sidebar" or "Close Sidebar". Collapsed sidebar content uses `display: none` and `aria-hidden`, so keyboard focus stays on the toggle. Below 1024px the sidebar is a button named with `currentPageTitle`, or "Menu" when that prop is omitted. Activating it opens a popover, and choosing a link inside the popover closes it.

    Title editing replaces the heading with a text field. Escape cancels and restores the previous title. Enter confirms the edit unless `onChange` returns `false`. The edit, confirm, and cancel controls have accessible names. Override them with `editButtonLabel`, `editFieldLabel`, `confirmButtonLabel`, and `cancelButtonLabel`.

    Secondary actions that collapse into the overflow control use a button named "More actions". A primary action that passes `items` is a menu trigger. Both follow [Menu](/docs/web/components/menu/code) keyboard behavior.

    A panel with `open={false}` sets `aria-hidden` and `aria-disabled`. Below 1024px, a controlled panel renders in a dialog and uses that dialog's focus trap. On large screens, an open controlled panel sets `tabIndex={0}` on its scroll region so Page Up and Page Down can scroll it. Omitting `open` keeps the panel visible in the layout, and the panel still sets `aria-hidden` because that attribute is `!open`. Persistent panel content is visible and removed from the accessibility tree.

    On the beta `Page`, the `main` landmark is a tab stop at 1024px and wider. Focus it, then use Page Up and Page Down to scroll the content.
  </Tab>

  <Tab title="Page Props">
    ```tsx theme={null}
    <Page>
      <Page.Sidebar>...</Page.Sidebar>
      <Page.Header title="Page Title" />
      <Page.Content>...</Page.Content>
    </Page>
    ```

    ## `Page` Props

    The `Page` component can accept any valid HTML `div` props.
  </Tab>

  <Tab title="Page.Sidebar Props">
    ```tsx theme={null}
    <Page.Sidebar localStorageKey="sidebar-collapse">
      <Page.SidebarHeader>Header</Page.SidebarHeader>
      <SideNav>...</SideNav>
    </Page.Sidebar>
    ```

    ## `Page.Sidebar` Props

    In addition to the props listed below, the `Page.Sidebar` component can accept any valid HTML `div` props.

    <ParamField path="currentPageTitle" type="string" default="Menu">
      Text on the trigger that opens the sidebar popover below 1024px. An empty
      string also falls back to "Menu".
    </ParamField>

    <ParamField path="data-tracking-id" type="string">
      On the desktop sidebar, applies this value to the sidebar root and skips
      the generated tracking ID on the collapse button. The adaptive popover
      does not receive it.
    </ParamField>

    <ParamField path="localStorageKey" type="string" default="sidebar-collapsed">
      `localStorage` key for the desktop collapsed state. See [Sidebar collapsed
      state in local storage](#sidebar-collapsed-state-in-local-storage).
    </ParamField>
  </Tab>

  <Tab title="Page.SidebarHeader Props">
    ```tsx theme={null}
    <Page.SidebarHeader>
      <Text variant="headline" el="h2">
        Header
      </Text>
    </Page.SidebarHeader>
    ```

    ## `Page.SidebarHeader` Props

    The `Page.SidebarHeader` component can accept any valid HTML `div` props.
  </Tab>

  <Tab title="Page.Header Props">
    ```tsx theme={null}
    <Page.Header
      title="Page Title"
      breadcrumbs={breadcrumbs}
      chips={chips}
      description="Page description"
      actions={{
        primary: { label: "Primary Action", onClick: () => {} },
        secondary: [{ label: "Secondary Action", onClick: () => {} }],
      }}
      preferenceAction={{
        "aria-label": "Open settings",
        onClick: () => {},
      }}
    />
    ```

    ## `Page.Header` Props

    The `Page.Header` component can accept any valid HTML `header` props, as well as the following:

    <ParamField path="title" type="string | { text: string; onChange?: (title: string) => void | boolean; editButtonLabel?: string; editFieldLabel?: string; confirmButtonLabel?: string; cancelButtonLabel?: string; }" required>
      Page `h1`. A string is read-only. Pass an object with `onChange` to show an edit button. `onChange` receives the confirmed title and can keep the editor open by returning `false`.

      Accessible names for the edit controls default to "Edit title", "Page title", "Update title", and "Cancel" for `editButtonLabel`, `editFieldLabel`, `confirmButtonLabel`, and `cancelButtonLabel`.
    </ParamField>

    <ParamField path="actions" type={`{ primary?: (Omit<ButtonProps, "appearance"> & { label: string }) | (Omit<Omit<ButtonProps, "appearance"> & { label: string }, "onClick"> & { items: (Omit<ButtonProps, "appearance"> & { label: string })[] }); secondary?: (Omit<ButtonProps, "appearance"> & { label: string })[]; }`}>
      Primary and secondary controls. `label` is the visible text on every action. `appearance` is omitted because the header sets primary and secondary emphasis.

      Three or more secondary actions collapse into a More actions menu. Two or more secondary actions also collapse when a primary action is present.

      Pass `items` instead of `onClick` on `primary` to make that button open a menu. The trigger shows a trailing chevron, which replaces a consumer `icon`. Menu items keep a bare icon or `{ after }`. An `{ before }` icon is dropped, because `Menu.Item` only renders a leading icon.
    </ParamField>

    <ParamField path="breadcrumbs" type="BreadcrumbsLinkProps[]">
      Links rendered above the title. Each item is spread onto `Breadcrumbs.Link`.
    </ParamField>

    <ParamField path="chips" type="ChipProps[]">
      Status chips rendered under the title.
    </ParamField>

    <ParamField path="description" type="ReactNode">
      Full-width content under the title and actions. Use text and links here. Put page actions in `actions`.
    </ParamField>

    <ParamField path="layout" type="LayoutProps">
      Configures the header's built-in [Layout](/docs/web/components/layout/code). When unset, the header uses the default fixed layout with a `1280px` max width. Match the `fluid` and `variant` values of the `Layout` inside `Page.Content`.
    </ParamField>

    <ParamField path="preferenceAction" type={`(Omit<ButtonProps, "aria-label" | "icon" | "appearance" | "size" | "className"> & { "aria-label": string }) | (Omit<ButtonLinkProps, "aria-label" | "icon" | "appearance" | "size" | "className"> & { "aria-label": string })`}>
      Settings control beside the title. `aria-label` is required and is also the tooltip text. The header supplies the settings icon, ghost appearance, small size, and class name. Pass `href` to render a link. Otherwise it renders a button.
    </ParamField>
  </Tab>

  <Tab title="Page.Content Props">
    ```tsx theme={null}
    <Page.Content>
      <Layout>
        <Layout.Item span={12}>Content</Layout.Item>
      </Layout>
    </Page.Content>
    ```

    ## `Page.Content` Props

    `Page.Content` renders a `section` and accepts HTML `section` attributes. Inside `Page`, it portals into the shell's `main` landmark and renders nothing until that slot mounts. Outside `Page`, it renders in place.
  </Tab>

  <Tab title="Page.Panel Props">
    ```tsx theme={null}
    <Page.Panel open={true} size="medium" noPadding={false}>
      Panel content
    </Page.Panel>
    ```

    ## `Page.Panel` Props

    `Page.Panel` renders a `div` and accepts HTML `div` attributes, as well as the following:

    <ParamField path="defaultDrillDownIndex" type="number" default="0">
      Screen the first `next()` opens from a closed [DrillDown](/docs/web/components/drilldown/code) stack. The panel starts with no active screen. Closing the panel restores this index.
    </ParamField>

    <ParamField path="noPadding" type="boolean" default="false">
      Removes the panel's inner padding.
    </ParamField>

    <ParamField path="onDrillDownContextChange" type="(context: DrillDownContextProps) => void">
      Receives the panel's DrillDown context. Spread `drillDownProps` from `useDrillDown()` onto the panel instead of wiring this callback directly.
    </ParamField>

    <ParamField path="open" type="boolean">
      Controlled visibility. Omit it for a persistent panel that stays in the layout. Below 1024px, a panel with `open` set renders inside a dialog.
    </ParamField>

    <ParamField path="size" type={`"small" | "medium" | "large"`} default="medium">
      Desktop width. `"small"` is 16rem, `"medium"` is 24rem, and `"large"` is 30rem.
    </ParamField>
  </Tab>

  <Tab title="Page.Panel.Header Props">
    ```tsx theme={null}
    import { Text } from "@servicetitan/anvil2";
    import { Page } from "@servicetitan/anvil2/beta";

    <Page.Panel.Header>
      <Text variant="headline" el="h2">
        Details
      </Text>
    </Page.Panel.Header>
    ```

    ## `Page.Panel.Header` Props

    Available on the beta `Page` from `@servicetitan/anvil2/beta`.

    Renders a `header` pinned above the panel's scrolling regions. Its measured height offsets sticky `Page.Panel.Content`. Accepts HTML `header` attributes. It has no other props.
  </Tab>

  <Tab title="Page.Panel.Content Props">
    ```tsx theme={null}
    import { Page } from "@servicetitan/anvil2/beta";

    <Page.Panel.Content sticky>
      Filters
    </Page.Panel.Content>
    ```

    ## `Page.Panel.Content` Props

    Available on the beta `Page` from `@servicetitan/anvil2/beta`.

    Renders a `div` and accepts HTML `div` attributes, as well as the following:

    <ParamField path="sticky" type="boolean" default="false">
      Pins this region below `Page.Panel.Header` while the panel scrolls. The offset tracks the header's height.
    </ParamField>
  </Tab>

  <Tab title="Page.Panel.Footer Props">
    ```tsx theme={null}
    import { Button } from "@servicetitan/anvil2";
    import { Page } from "@servicetitan/anvil2/beta";

    <Page.Panel.Footer sticky>
      <Button appearance="primary">Save</Button>
    </Page.Panel.Footer>
    ```

    ## `Page.Panel.Footer` Props

    Available on the beta `Page` from `@servicetitan/anvil2/beta`.

    Renders a `footer`. Actions wrap and align to the end. Accepts HTML `footer` attributes, as well as the following:

    <ParamField path="sticky" type="boolean" default="false">
      Pins the footer to the bottom of the panel.
    </ParamField>
  </Tab>

  <Tab title="Page.Footer Props">
    ```tsx theme={null}
    <Page.Footer>
      Footer content
    </Page.Footer>
    ```

    ## `Page.Footer` Props

    Renders an HTML `footer` and accepts HTML `div` attributes plus [layout utility props](/docs/web/utilities/layout-props). Inside `Page`, it portals into the footer slot and renders nothing until that slot mounts. Outside `Page`, it renders in place. The footer sticks to the bottom of the content area.
  </Tab>
</Tabs>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.