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

# Card – Code

> Cards are containers that group related content together.

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="card-playground" fullWidth screenshot>
      ```tsx lines expandable theme={null}
      import { Card, Text } from "@servicetitan/anvil2";

      function App() {
        return (
          <Card flexDirection="column" background="strong">
            <Text variant="headline" el="h2" size="large">
              Basic Card
            </Text>
            <Text>
              This is the text within this example card. Make sure to always add{" "}
              <code>flexDirection=&quot;column&quot;</code> to the <code>Card</code>{" "}
              to make the text stack vertically.
            </Text>
          </Card>
        );
      }

      export default App;
      ```
    </LiveCode>

    ## Common Examples

    Cards are used to distinguish sections of content within a page, and can be interactive (clickable).

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

    function ExampleComponent() {
      return (
        <Card flexDirection="column">
          <Text variant="headline" el="h2">
            Card Header
          </Text>
          <Text>Card body text.</Text>
        </Card>
      );
    }
    ```

    ### Background Color

    Use the `background` prop to control the background color of the card.

    <LiveCode showCode example="card-background" screenshot fullWidth>
      ```tsx lines expandable theme={null}
      import { Card, Flex } from "@servicetitan/anvil2";

      function App() {
        return (
          <Flex gap={6} direction="column">
            <Card>
              Card with a <br />
              default background
            </Card>
            <Card background="strong">
              Card with a <br />
              strong background
            </Card>
            <Card background="stronger">
              Card with a <br />
              stronger background
            </Card>
          </Flex>
        );
      }

      export default App;
      ```
    </LiveCode>

    ### Padding

    Anvil provides five options to pass to the `padding` prop to determine the spacing between the card border and content.

    <LiveCode showCode example="card-padding" screenshot fullWidth>
      ```tsx lines expandable theme={null}
      import { Card, Flex } from "@servicetitan/anvil2";

      function App() {
        return (
          <Flex gap={6} direction="column" alignItems="flex-start">
            <Card padding="0">This card has no padding applied to it.</Card>
            <Card padding="xsmall">This card has 4px padding applied to it.</Card>
            <Card padding="small">This card has 8px padding applied to it.</Card>
            <Card padding="medium">This card has 16px padding applied to it.</Card>
            <Card padding="large">This card has 24px padding applied to it.</Card>
          </Flex>
        );
      }

      export default App;
      ```
    </LiveCode>

    ### Flex Direction

    Use the `flexDirection` prop to determine which direction the `children` are displayed. The parent `div` of an Anvil `Card` uses `display: inline-flex`, so note that the orientation of the `children` could vary based on the user's locale and settings.

    <LiveCode showCode example="card-flexdirection-row" screenshot fullWidth>
      ```tsx lines expandable theme={null}
      import { Card } from "@servicetitan/anvil2";

      function App() {
        return (
          <Card flexDirection="row" style={{ height: 100, gap: 8, width: "100%" }}>
            <div
              style={{ flex: 2, background: "var(--background-color-strongest)" }}
            />
            <div
              style={{ flex: 1, background: "var(--background-color-strongest)" }}
            />
          </Card>
        );
      }

      export default App;
      ```
    </LiveCode>

    <LiveCode showCode example="card-flexdirection-column" screenshot fullWidth>
      ```tsx lines expandable theme={null}
      import { Card } from "@servicetitan/anvil2";

      function App() {
        return (
          <Card flexDirection="column" style={{ height: 100, gap: 8, width: "100%" }}>
            <div
              style={{ flex: 2, background: "var(--background-color-strongest)" }}
            />
            <div
              style={{ flex: 1, background: "var(--background-color-strongest)" }}
            />
          </Card>
        );
      }

      export default App;
      ```
    </LiveCode>

    ### Creating sections with Divider

    To create sections within a `Card`, use a `padding` of `0` along with the `Divider`, then give each child element its own padding as needed.

    <LiveCode showCode example="card-divider" screenshot fullWidth>
      ```tsx lines expandable theme={null}
      import { Card, Text, Divider } from "@servicetitan/anvil2";

      function App() {
        return (
          <Card padding="0" flexDirection="column">
            <Text className="p-3">Section 1. Lorem ipsum dolor sit amet.</Text>

            <Divider />

            <Text className="p-3">Section 2. Lorem ipsum dolor sit amet.</Text>
          </Card>
        );
      }

      export default App;
      ```
    </LiveCode>

    ### AI Mark

    Place the [AI Mark](/docs/web/components/ai-mark/code) inline with a card title by setting `aiMark` on [`Text`](/docs/web/components/text/code) with `variant="headline"`. Use `true` for the sparkle only, or pass tooltip or popover configuration for more detail.

    <Note>
      When a card is wrapped in `ButtonCompound`, avoid interactive AI Mark configurations (tooltip or popover triggers) inside the card if that would nest interactive elements incorrectly. For clickable cards that need an interactive mark, use the [Interactive Card](/docs/web/components/interactive-card) pattern and validate with accessibility testing.
    </Note>

    <LiveCode showCode example="ai-mark-card" screenshot fullWidth>
      ```tsx lines expandable theme={null}
      import { Card, List, Text } from "@servicetitan/anvil2";

      function App() {
        return (
          <Card flexDirection="column" style={{ maxWidth: "500px" }}>
            <Text variant="headline" el="h2" aiMark>
              Daily Job Summary
            </Text>
            <Text>
              You have four jobs on the board for the next two days: two drain
              cleanouts (Oak St., Riverside Ave.), a water-heater diagnostic on Maple,
              and a leak under a kitchen sink on Cedar.
            </Text>
            <br />
            <Text variant="headline" el="h3" size="small">
              High Priority Jobs
            </Text>
            <List>
              <List.Item>Drain Cleanout (Oak St.)</List.Item>
              <List.Item>Leak Under a Kitchen Sink (Cedar)</List.Item>
            </List>
          </Card>
        );
      }

      export default App;
      ```
    </LiveCode>

    ### Interactive Cards

    If the card needs to have an action applied to it, please use the [InteractiveCard component](/docs/web/components/interactive-card) instead.

    ## React Accessibility

    * Since the `Card` component can accept any valid HTML `div` props, all relevant `aria-*` props are supported.
    * When creating an interactive card, the `ButtonCompound` component wraps the rendered card in a `button` element, and will use the content of the card to determine its label. If the card includes complex content, it may be helpful to use `aria-labelledby` or `aria-label`. Always test using a screenreader to ensure the label makes sense and doesn't repeat information.

    For more guidance on creating accessible components, see [building accessible custom components best practices](/docs/accessibility/custom-components).
  </Tab>

  <Tab title="Card Props">
    ```tsx theme={null}
    <Card background="strong" padding="medium">
      Card content
    </Card>
    ```

    ## `Card` Props

    In addition to the props listed below, the `Card` component can accept any valid HTML `div` props and layout utility props (including `flexDirection`).

    <ParamField path="background" type={`"strong" | "stronger"`}>
      Uses the default app background color if no value is provided.
    </ParamField>

    <ParamField path="padding" type={`"0" | "xsmall" | "small" | "medium" | "large"`} default="medium" />
  </Tab>
</Tabs>
