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

# AI Mark - Web

> The AI mark is the standard visual indicator for AI-powered experiences in ServiceTitan web applications.

> [!NOTE]
>
> For development, import `AiMark` from
> `@servicetitan/anvil2`. For design, find it in the
> [Anvil2 file](https://www.figma.com/design/xaNQsvRKczoxu7pj0M6QT8/Anvil2-v3?node-id=2090-2873).

> [!NOTE]
>
> Embedded AI is a rapidly developing area at ServiceTitan. For how the AI Mark fits into broader embedded AI and Atlas usage, see [Embedded AI](https://anvil.servicetitan.com/docs/patterns/embedded-ai/web.md).

The AI Mark signals that a control, label, or surface was touched by AI. It replaces legacy indicators so AI branding stays consistent across the product.

ai-mark-banner

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/ai-mark-banner?file=src/App.tsx)

## Anatomy

The AI Mark consists of a single element: the double-star shape that identifies AI-assisted or AI-generated content in product UI.

![Anatomy of the AI Mark (light theme)](https://mintcdn.com/servicetitan/QifcvLALFDifjNIm/images/docs/web/components/ai-mark/ai-mark-anatomy-light.png?fit=max\&auto=format\&n=QifcvLALFDifjNIm\&q=85\&s=e9879d8203eaf32dca0dcd22e17a1687)

1. **Double-star shape** — AI Mark

## Options

The AI Mark supports the following configurations for color, size, and related presentation.

### Color

* **Gradient**: A blue gradient, used as the primary option for denoting something is AI.
* **Default**: A `currentColor` option, used against non-default backgrounds, such as within buttons.

ai-mark-banner

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/ai-mark-banner?file=src/App.tsx)

### Sizing

The mark comes in four sizes aligned with Anvil icon sizing.

ai-mark-sizes

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/ai-mark-sizes?file=src/App.tsx)

* **small**: 12×12
* **medium**: 16×16
* **large**: 24×24
* **xlarge**: 32×32

## Behavior

### Animation

[ai-mark-animations](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/ai-mark-animations?embed=1\&hideNavigation=1\&hideExplorer=1\&terminalHeight=0\&file=src/App.tsx)

The AI Mark has two animation modes:

* **Pulse**: The default hover treatment for the mark.
* **Spin**: Used exclusively in situations that perform an action (such as buttons), also on hover.

### Tooltips and popovers

The AI Mark responds to hover and focus, and it supports an optional tooltip or popover disclosure when users need more context than the icon alone.

Pair the AI Mark with a tooltip or popover when short inline copy is not enough and users should be able to read more about how AI is used.

* **Tooltip**: Use for a single line or short string of explanatory text on hover or keyboard focus. Reserve tooltips for lightweight context (for example, what “AI-assisted” means in that spot).
* **Popover**: Use when the disclosure needs richer layout or actions—longer explanations, multiple lines, or controls such as feedback. Popover opens on activation (for example, click), not only on hover.

With either pattern, the mark uses an interactive treatment (including animation on hover and focus) and a larger hit target than the plain icon.

ai-mark-tooltip

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/ai-mark-tooltip?file=src/App.tsx)

For configuration options and examples, see [AI Mark – Code](https://anvil.servicetitan.com/docs/components/ai-mark/code).

### Contrast-driven style choice

Use the gradient mark by default. Switch to the monochrome mark when the primary treatment fails contrast requirements—for example, primary Button and similar cases where the gradient does not meet contrast against the background.

### Editable states

The AI Mark responds to edits. When a user starts editing a cell, remove the AI Mark immediately. After editing begins, the value is no longer purely AI-generated, and keeping the mark would misrepresent the content.

## Usage Guidelines

Use the AI Mark when you need a clear, consistent visual that an experience or control is AI-related outside of the dedicated Atlas conversational UI.

| Standard | Requirement |
| - | - |
| DoLabeling AI-assisted content | Label AI-assisted actions or fields in embedded product UI |
| DoPairing with Anvil2 AI affordances | Pair with component-level AI affordances built into Anvil2 patterns |
| Don'tOutdated marks | Don't use legacy AI icons or emoji, such as single sparkles, TI logos, or sparkle emojis; use the double-star AI Mark instead; see [Don't use outdated marks](#dont-use-outdated-marks) |
| Don'tNon-AI features | Use neutral icons or no AI indicator so users are not misled |
| Don'tAtlas-only flows | Within the centralized conversational Atlas experience, follow Atlas-specific guidance; see [Embedded AI vs Atlas](https://anvil.servicetitan.com/docs/patterns/embedded-ai/web.md#embedded-ai-vs-atlas) |
| Don'tAvoid duplicate marks | If the mark already appears on the control that caused the content to appear, omit it from the generated content; see [Avoid duplicate marks](#avoid-duplicate-marks) |

### Placement on fields

The AI Mark can sit in the field label or in the body or description area. When a field has a visible label, prefer the label. When there is no label—for example, a Data Table cell—place the mark to the right of what AI has generated. If a group of items was generated with AI (for example, a Card), place the mark to the right of the group title.

### Avoid duplicate marks

If the AI Mark already appears on a triggering element (for example, a button that starts an AI action), do not show it again in the output that element produces. One clear association per action keeps the UI honest and avoids noisy repetition.

### Don't use outdated marks

![AI mark to use](https://mintcdn.com/servicetitan/gDHlLI8IubZTbnh1/images/docs/web/patterns/embedded-ai/mark-do.png?fit=max\&auto=format\&n=gDHlLI8IubZTbnh1\&q=85\&s=36d3b604184e78f35d5b52f672b39ec4)

**Do**
Use the AI Mark (double star) as the standard AI labeling indicator, replacing any single sparkle, TI logo, or sparkle emoji for consistent AI branding across the application.

![Outdated or incorrect AI marks](https://mintcdn.com/servicetitan/gDHlLI8IubZTbnh1/images/docs/web/patterns/embedded-ai/mark-dont.png?fit=max\&auto=format\&n=gDHlLI8IubZTbnh1\&q=85\&s=01dbc857917b50f65455e6bd5b6bb062)

**Don’t**
Use outdated or incorrect AI marks such as single sparkles, TI logos, or sparkle emojis. Use the AI Mark component instead.

## Content

Content within or next to the AI Mark should keep AI disclosure clear and scannable. Prefer plain language that states what the AI does or what the user should expect.

* Pair the mark with labels or headings that describe the AI-assisted outcome, not the technology
* In complex workflows, add short explanatory text or follow-up disclosure as needed in the UI or via tooltip or popover when the mark alone does not carry enough context
* When both a label and description exist, align the mark with [Placement on fields](#placement-on-fields)

## Keyboard Interaction

Users can navigate the AI Mark using the same keyboard patterns as the surrounding control or container (for example, the button or field label that includes the mark).

> **[Embedded AI](https://anvil.servicetitan.com/docs/patterns/embedded-ai/web.md)**
>
> Read more about embedded AI and how to use the AI Mark within larger workflows in the product.

## Code

**Implementation**

ai-mark-playground

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/ai-mark-playground?file=src/App.tsx)

```tsx lines expandable theme={null}
import { AiMark, Flex, Text } from "@servicetitan/anvil2";

function App() {
  return (
    <Flex gap="2">
      <AiMark type="gradient" size="large" />
      <Text variant="headline" size="medium" el="h2">
        Created by Atlas
      </Text>
    </Flex>
  );
}

export default App;
```

## Common Examples

```tsx theme={null}
import { AiMark } from "@servicetitan/anvil2";

function ExampleComponent() {
  return (
    <AiMark
      type="gradient"
      popoverOrTooltipConfig={{
        type: "tooltip",
        content: "These suggestions were written by Atlas.",
        triggerLabel: "Information about AI-powered suggestions",
      }}
    />
  );
}
```

`AiMark` displays the ServiceTitan AI mark. When paired with a tooltip or popover via `popoverOrTooltipConfig`, the icon becomes interactive and animates on hover and focus. Without a configuration, it renders as a static inline icon.

The component automatically respects the user's `prefers-reduced-motion` system preference — animations are disabled when reduced motion is preferred.

### With Tooltip

Use the tooltip variant to show a brief text label when users hover or focus the icon.

ai-mark-tooltip

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/ai-mark-tooltip?file=src/App.tsx)

```tsx lines expandable theme={null}
import { AiMark, Flex, Text } from "@servicetitan/anvil2";

function App() {
  return (
    <Flex alignItems="center" gap="3">
      <Text>Call & job notes summary</Text>
      <AiMark
        type="gradient"
        popoverOrTooltipConfig={{
          type: "tooltip",
          content:
            "Drafted from your recorded calls and past visits. Have your team verify details before the customer sees them.",
          triggerLabel: "How AI summarizes technician notes",
        }}
      />
    </Flex>
  );
}

export default App;
```

### With Popover

Use the popover variant to show richer content — such as a title and description — when users click the icon.

ai-mark-popover

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/ai-mark-popover?file=src/App.tsx)

```tsx lines expandable theme={null}
import { AiMark, Button, Flex, Text, Textarea } from "@servicetitan/anvil2";

function App() {
  return (
    <Flex alignItems="center" gap="3">
      <Text>Recommended estimate lines</Text>
      <AiMark
        type="gradient"
        popoverOrTooltipConfig={{
          type: "popover",
          triggerLabel: "Feedback on AI estimate suggestions",
          props: {
            placement: "right",
          },
          content: (
            <Flex direction="column" gap="2">
              <Textarea
                label="Was this suggestion helpful for your trade?"
                rows={3}
              />
              <Flex gap="2" justifyContent="flex-end">
                <Button size="small" appearance="primary" onClick={() => {}}>
                  Submit
                </Button>
              </Flex>
            </Flex>
          ),
        }}
      />
    </Flex>
  );
}

export default App;
```

### Force Animated

Use `forceAnimate` to control the pulsing animation from a parent component. This is intended for cases where `AiMark` is embedded inside an interactive element and the animation should respond to the parent's hover or focus state rather than its own.

```tsx theme={null}
import { useState } from "react";
import { AiMark, Flex, Text } from "@servicetitan/anvil2";

function ParentComponent() {
  const [isHovered, setIsHovered] = useState(false);

  return (
    <Flex
      alignItems="center"
      gap="2"
      onMouseEnter={() => setIsHovered(true)}
      onMouseLeave={() => setIsHovered(false)}
    >
      <AiMark type="gradient" forceAnimate={isHovered} />
      <Text>AI-powered suggestions</Text>
    </Flex>
  );
}
```

### Plain Icon

Without `popoverOrTooltipConfig`, `AiMark` renders as a non-interactive icon. Use this when you only need the visual indicator without any overlay.

ai-mark-plain

[StackBlitz demo](https://stackblitz.com/github/servicetitan/anvil2-docs-live-code/tree/main/examples/ai-mark-plain?file=src/App.tsx)

```tsx lines expandable theme={null}
import { AiMark, Flex, Text } from "@servicetitan/anvil2";

function App() {
  return (
    <Flex alignItems="center" gap="2">
      <AiMark type="gradient" />
      <Text>Created by Atlas</Text>
    </Flex>
  );
}

export default App;
```

## React Accessibility

* When `popoverOrTooltipConfig` is provided, the icon renders as a ghost `Button` with an `aria-label` set from `triggerLabel`. This ensures screen readers announce a meaningful label rather than just "button".
* Pass a descriptive `triggerLabel` that communicates the purpose of the overlay — for example, `"Learn about AI-powered suggestions"` rather than `"AI icon"`.
* Animations are automatically suppressed when the user has `prefers-reduced-motion: reduce` set in their system preferences.
* The popover variant uses the internal beta `Popover` with `role="dialog"`; focus moves into the popover when it opens.
* The popover variant calls `stopPropagation` on click to prevent unintended event bubbling when the icon is nested inside other interactive elements.

**AiMark Props**

```tsx theme={null}
<AiMark
  collapsePadding="inline"
  type="default"
  size="medium"
  popoverOrTooltipConfig={{
    type: "tooltip",
    content: "Powered by AI",
    triggerLabel: "AI feature information",
  }}
/>
```

## `AiMark` Props

`AiMark` accepts all props from `Icon` except `svg`, plus the following:

**collapsePadding** `"inline" | "block" | "all"`

Collapses layout padding on the chosen axes so the mark’s box matches a plain icon on those axes. Interactive tooltip and popover variants keep the same hit target; non-interactive variants add padding on the non-collapsed axes.

**forceAnimate** `boolean` (default: false)

Forces the pulsing morph animation to play.

**popoverOrTooltipConfig** `AiMarkWithTooltipOrPopoverConfig`

Configuration for an optional tooltip or popover overlay. When provided, the icon renders as an interactive button that animates on hover and focus. See `AiMarkWithTooltipOrPopoverConfig` below.

**size** `"small" | "medium" | "large" | "xlarge"` (default: medium)

Size of the icon.

**type** `"default" | "gradient"` (default: default)

Visual style of the AI mark. `"default"` inherits the current text color. `"gradient"` applies a blue gradient fill.

## `AiMarkWithTooltipOrPopoverConfig`

A discriminated union — use `type` to select either the tooltip or popover variant.

### Tooltip variant

**triggerLabel** `string` *required*

Accessible label for the trigger button, announced to screen readers.

**content** `string`

Text content displayed inside the tooltip. When omitted, defaults to `AI can make mistakes.`

**props** `Omit\<TooltipProps, "children"\>`

Optional props forwarded to the `Tooltip` component.

**type** `"tooltip"`

Discriminant for the tooltip variant.

### Popover variant

**triggerLabel** `string` *required*

Accessible label for the trigger button, announced to screen readers.

**content** `ReactNode`

Content rendered inside the popover. When omitted, defaults to the standard AI disclaimer message and a Learn more link to the privacy policy.

**contentProps** `Pick\<PopoverContentProps, "hideWhileClosed" | "scrollerRef"\>`

Optional props forwarded to `Popover.Content`.

**props** `Omit\<PopoverDialogRoleProps, "children"\>`

Optional props forwarded to the internal beta `Popover` root.

**type** `"popover"`

Discriminant for the popover variant.


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