> ## 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.

# Details – Design

> Details are accordions that show or hide information when clicked.

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>;
  }
};

export const CodePreviewPlaceholder = ({double, fullWidth}) => {
  const single = <div style={{
    width: fullWidth ? "100%" : "50%",
    borderRadius: "1rem",
    display: "flex",
    padding: "1rem",
    flexDirection: "column",
    gap: "0.5rem",
    height: "10rem",
    marginBlockEnd: "1rem"
  }} className="border-width-default border-color-subdued">
      <div className="bg-strong border-radius-large" style={{
    width: "100%",
    flexGrow: "1"
  }} />
      <div className="bg-strong border-radius-large" style={{
    width: "100%",
    flexGrow: "1"
  }} />
    </div>;
  return double ? <div style={{
    display: "flex",
    gap: "1rem"
  }}>
      {single}
      {single}
    </div> : single;
};

<Frame>
  <div className="w-full h-full bg-[#FFFFFF] p-2 rounded flex items-center justify-center">
    <img noZoom src="https://mintcdn.com/servicetitan/uz2PQSvO75TRhQ38/images/docs/web/components/shared/details-main-image.png?fit=max&auto=format&n=uz2PQSvO75TRhQ38&q=85&s=327ac14a3d65db4be6a08123ce240119" width="800" height="80" data-path="images/docs/web/components/shared/details-main-image.png" />
  </div>
</Frame>

## Anatomy

The Details consists of three primary elements that work together to show or hide information when clicked.

<Frame>
  <div className="w-full h-full bg-[#FFFFFF] p-2 rounded flex items-center justify-center">
    <img
      src="https://mintcdn.com/servicetitan/lX6UhRsjMdTeB7y6/images/docs/web/components/details/design/details-anatomy.png?fit=max&auto=format&n=lX6UhRsjMdTeB7y6&q=85&s=256eb3702e3c120de6ffab3f6629b704"
      alt="Details
anatomy"
      width="878"
      height="270"
      data-path="images/docs/web/components/details/design/details-anatomy.png"
    />
  </div>
</Frame>

1. Chevron
2. Heading
3. Body content

## Options

The Details supports flexible content configurations to accommodate various accordion scenarios.

<LiveCode example="details-playground" screenshot fullWidth>
  ```tsx lines theme={null}
  import { Details } from "@servicetitan/anvil2";

  function App() {
    return (
      <div style={{ maxWidth: "23rem" }}>
        <Details>
          <Details.Summary>Click to see details.</Details.Summary>
          <Details.Content>
            Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
            eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
            minim veniam, quis nostrud exercitation ullamco laboris nisi ut
            aliquip ex ea commodo consequat.
          </Details.Content>
        </Details>
        <Details>
          <Details.Summary>Click to see details.</Details.Summary>
          <Details.Content>
            Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
            eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
            minim veniam, quis nostrud exercitation ullamco laboris nisi ut
            aliquip ex ea commodo consequat.
          </Details.Content>
        </Details>
      </div>
    );
  }

  export default App;
  ```
</LiveCode>

## Behavior

The Details responds to user interaction with flexible opening and closing behaviors while handling content overflow.

### Closed by default

<LiveCode example="details-defaultexpanded" screenshot fullWidth>
  ```tsx lines theme={null}
  import { Details } from "@servicetitan/anvil2";

  function App() {
    return (
      <div style={{ maxWidth: "23rem" }}>
        <Details>
          <Details.Summary>Closed by default Details heading</Details.Summary>
          <Details.Content>
            Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
            eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
            minim veniam, quis nostrud exercitation ullamco laboris nisi ut
            aliquip ex ea commodo consequat.
          </Details.Content>
        </Details>
        <Details defaultExpanded>
          <Details.Summary>Open by default Details heading</Details.Summary>
          <Details.Content>
            Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
            eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
            minim veniam, quis nostrud exercitation ullamco laboris nisi ut
            aliquip ex ea commodo consequat.
          </Details.Content>
        </Details>
      </div>
    );
  }

  export default App;
  ```
</LiveCode>

Details are closed until clicked by default. Configure them to open by default when showing this content immediately.

### Opening behavior

<LiveCode example="details-accordion-false" screenshot fullWidth>
  ```tsx lines theme={null}
  import { Details } from "@servicetitan/anvil2";

  function App() {
    return (
      <div style={{ maxWidth: "23rem" }}>
        <Details>
          <Details.Summary>Independent Details</Details.Summary>
          <Details.Content>
            Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
            eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
            minim veniam, quis nostrud exercitation ullamco laboris nisi ut
            aliquip ex ea commodo consequat.
          </Details.Content>
        </Details>
        <Details>
          <Details.Summary>Independent Details</Details.Summary>
          <Details.Content>
            Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
            eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
            minim veniam, quis nostrud exercitation ullamco laboris nisi ut
            aliquip ex ea commodo consequat.
          </Details.Content>
        </Details>
        <Details>
          <Details.Summary>Independent Details</Details.Summary>
          <Details.Content>
            Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
            eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
            minim veniam, quis nostrud exercitation ullamco laboris nisi ut
            aliquip ex ea commodo consequat.
          </Details.Content>
        </Details>
      </div>
    );
  }

  export default App;
  ```
</LiveCode>

<LiveCode example="details-accordion" screenshot fullWidth>
  ```tsx lines theme={null}
  import { Details, type DetailsProps } from "@servicetitan/anvil2";
  import { useState } from "react";

  function App() {
    const [expanded, setExpanded] = useState<string | undefined>("2");
    const expandDetails = (id: DetailsProps["id"]) => {
      setExpanded((prev) => (prev === id ? undefined : id));
    };

    return (
      <div style={{ maxWidth: "23rem" }}>
        <Details
          id="1"
          expanded={expanded === "1"}
          onClick={() => expandDetails("1")}
        >
          <Details.Summary>One open at a time Details</Details.Summary>
          <Details.Content>
            Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
            eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
            minim veniam, quis nostrud exercitation ullamco laboris nisi ut
            aliquip ex ea commodo consequat.
          </Details.Content>
        </Details>
        <Details
          id="2"
          expanded={expanded === "2"}
          onClick={() => expandDetails("2")}
        >
          <Details.Summary>One open at a time Details</Details.Summary>
          <Details.Content>
            Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
            eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
            minim veniam, quis nostrud exercitation ullamco laboris nisi ut
            aliquip ex ea commodo consequat.
          </Details.Content>
        </Details>
        <Details
          id="3"
          expanded={expanded === "3"}
          onClick={() => expandDetails("3")}
        >
          <Details.Summary>One open at a time Details</Details.Summary>
          <Details.Content>
            Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
            eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
            minim veniam, quis nostrud exercitation ullamco laboris nisi ut
            aliquip ex ea commodo consequat.
          </Details.Content>
        </Details>
      </div>
    );
  }

  export default App;
  ```
</LiveCode>

By default, individual Details open and close independently of each other. Configure them to work like an accordion, where only 0 to 1 individual Details is open at once.

### Heading overflow

<LiveCode example="details-overflow-summary" screenshot fullWidth>
  ```tsx lines theme={null}
  import { Details } from "@servicetitan/anvil2";

  function App() {
    return (
      <div style={{ maxWidth: "23rem" }}>
        <Details>
          <Details.Summary>
            This is a very long Details heading that will wrap when there is not
            enough room for it to be on one line.
          </Details.Summary>
          <Details.Content>
            Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
            eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
            minim veniam, quis nostrud exercitation ullamco laboris nisi ut
            aliquip ex ea commodo consequat.
          </Details.Content>
        </Details>
      </div>
    );
  }

  export default App;
  ```
</LiveCode>

Long headings will wrap when there is not enough room for it to be on one line.

The chevron indicator will be vertically centered with the height of the heading.

### Content overflow

<LiveCode example="details-overflow-content" screenshot fullWidth>
  ```tsx lines theme={null}
  import { Details } from "@servicetitan/anvil2";

  function App() {
    return (
      <div style={{ maxWidth: "23rem" }}>
        <Details defaultExpanded>
          <Details.Summary>Without a max height</Details.Summary>
          <Details.Content>
            Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
            eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
            minim veniam, quis nostrud exercitation ullamco laboris nisi ut
            aliquip ex ea commodo consequat.
          </Details.Content>
        </Details>
        <Details defaultExpanded>
          <Details.Summary>With a max height</Details.Summary>
          <Details.Content maxHeight="3.3rem">
            Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
            eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
            minim veniam, quis nostrud exercitation ullamco laboris nisi ut
            aliquip ex ea commodo consequat.
          </Details.Content>
        </Details>
      </div>
    );
  }

  export default App;
  ```
</LiveCode>

Body content for Details will display in full unless a specific height is set. When a height is set, the body content will appear with a scrollbar.

## Usage Guidelines

Use the Details when showing or hiding additional information in a collapsible accordion format.

### When to Use

Details are a collapsible accordion for showing or hiding additional information. They are also helpful in contexts where vertical space is limited.

#### Provide additional context

Details should be used as supplementary information that is not critical to completing a task.

<LiveCode example="details-supplementary" screenshot fullWidth>
  ```tsx lines theme={null}
  import { Details } from "@servicetitan/anvil2";

  function App() {
    return (
      <div style={{ maxWidth: "23rem" }}>
        <Details>
          <Details.Summary>How do I reset my password?</Details.Summary>
          <Details.Content>
            Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
            eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
            minim veniam, quis nostrud exercitation ullamco laboris nisi ut
            aliquip ex ea commodo consequat.
          </Details.Content>
        </Details>
      </div>
    );
  }

  export default App;
  ```
</LiveCode>

### When not to use

Details should not be used to hide information essential for the user to complete a task. It should also not be used to conceal lengthy content. It is recommended to use Tabs in this case.

### Alternatives

#### Details vs Tab

Details are used to show additional information around a sub-topic on a page, while a Tab categorizes information. The context of Details is small, non-essential information, while Tabs can categorize both small and large, essential and non-essential information.

### How to Use

#### In a group with other Details

Use Details as supplementary information that is not critical to completing a task.

<LiveCode example="details-supplementary-group" screenshot fullWidth>
  ```tsx lines theme={null}
  import { Details } from "@servicetitan/anvil2";

  function App() {
    return (
      <div style={{ maxWidth: "23rem" }}>
        <Details>
          <Details.Summary>How do I reset my password?</Details.Summary>
          <Details.Content>
            Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
            eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
            minim veniam, quis nostrud exercitation ullamco laboris nisi ut
            aliquip ex ea commodo consequat.
          </Details.Content>
        </Details>
        <Details>
          <Details.Summary>Where do I find my member ID?</Details.Summary>
          <Details.Content>
            Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
            eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
            minim veniam, quis nostrud exercitation ullamco laboris nisi ut
            aliquip ex ea commodo consequat.
          </Details.Content>
        </Details>
        <Details>
          <Details.Summary>When does my account expire?</Details.Summary>
          <Details.Content>
            Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
            eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
            minim veniam, quis nostrud exercitation ullamco laboris nisi ut
            aliquip ex ea commodo consequat.
          </Details.Content>
        </Details>
      </div>
    );
  }

  export default App;
  ```
</LiveCode>

## Content

Content within the Details should provide supplementary information that enhances understanding without being critical to task completion.

## Keyboard Interaction

Users can navigate the Details using standard keyboard controls.

| Key            | Description                                                       |
| -------------- | ----------------------------------------------------------------- |
| Tab            | Move to the next tab-able element within the Details or the page. |
| Space or Enter | Opens/Closes the Details component                                |
