# ServiceTitan Anvil — système de conception

## ServiceTitan Anvil — aperçu de l’interface du système de conception

Basculez entre les apparences claire et sombre, sélectionnez le personnel disponible et consultez un plan local synchronisé.

## ServiceTitan Anvil — référence de conception du système de conception

Exports du thème principal de @servicetitan/hammer-token 3.5.0, avec la documentation de thématisation d’Anvil2.

### Paires sémantiques pour les thèmes clair et sombre

| Nom / règle | Valeur / configuration | Utilisation |
| --- | --- | --- |
| BackgroundColorDefault | light #ffffff / dark #1a1a1a | Surface principale de l’application. |
| BackgroundColorDefaultHover | light #f9f9f9 / dark #292929 | Surface au survol. |
| BackgroundColorPrimary | light #0265dc / dark #70b1ff | Fond de l’action principale. |
| BackgroundColorPrimaryHover | light #1d4ca3 / dark #9ecaff | Rôle natif pour l’état de survol principal. |
| BackgroundColorPrimaryFaint | light #fafcff / dark #000b19 | Surface de sélection discrète. |
| ForegroundColorSubdued | light #707070 / dark #c4c4c4 | Texte d’accompagnement. |
| ForegroundColorOnPrimary | light #ffffff / dark #1a1a1a | Texte associé au remplissage principal. |
| ForegroundColorPrimary | light #0265dc / dark #70b1ff | Premier plan interactif. |
| BorderColorSubdued | light #e6e6e6 / dark #404040 | Bordures peu mises en avant. |
| BorderColorPrimary | light #0265dc / dark #9ecaff | Bordure interactive. |
| StatusColorInfo | light #0265dc / dark #70b1ff | État informatif. |
| StatusColorDanger | light #d62100 / dark #ff5b3d | Attention immédiate. |
| StatusColorSuccess | light #077e50 / dark #0bd085 | Vérification locale terminée. |
| StatusColorWarning | light #ffbe00 / dark #ffe278 | Mise en garde sans urgence. |
### Géométrie primitive et valeurs adaptatives

| Nom / règle | Valeur / configuration | Utilisation |
| --- | --- | --- |
| BreakpointSm | 640px | Primitif publié ; les alias sémantiques et de composants déterminent les rôles réellement appliqués. |
| BreakpointMd | 768px | Primitif publié ; les alias sémantiques et de composants déterminent les rôles réellement appliqués. |
| BreakpointLg | 1024px | Primitif publié ; les alias sémantiques et de composants déterminent les rôles réellement appliqués. |
| BreakpointXl | 1280px | Primitif publié ; les alias sémantiques et de composants déterminent les rôles réellement appliqués. |
| BreakpointXxl | 1536px | Primitif publié ; les alias sémantiques et de composants déterminent les rôles réellement appliqués. |
| DurationDefault | 200ms | Primitif publié ; les alias sémantiques et de composants déterminent les rôles réellement appliqués. |
| DurationInstant | 0ms | Primitif publié ; les alias sémantiques et de composants déterminent les rôles réellement appliqués. |
| DurationFast | 100ms | Primitif publié ; les alias sémantiques et de composants déterminent les rôles réellement appliqués. |
| DurationSlow | 300ms | Primitif publié ; les alias sémantiques et de composants déterminent les rôles réellement appliqués. |
| Radius0 | 0rem | Primitif publié ; les alias sémantiques et de composants déterminent les rôles réellement appliqués. |
| Radius1 | 0.1875rem | Primitif publié ; les alias sémantiques et de composants déterminent les rôles réellement appliqués. |
| Radius2 | 0.375rem | Primitif publié ; les alias sémantiques et de composants déterminent les rôles réellement appliqués. |
| Radius3 | 0.5625rem | Primitif publié ; les alias sémantiques et de composants déterminent les rôles réellement appliqués. |
| Radius4 | 0.75rem | Primitif publié ; les alias sémantiques et de composants déterminent les rôles réellement appliqués. |
| Radius5 | 0.9375rem | Primitif publié ; les alias sémantiques et de composants déterminent les rôles réellement appliqués. |
| Radius6 | 1.125rem | Primitif publié ; les alias sémantiques et de composants déterminent les rôles réellement appliqués. |
| Radius7 | 1.3125rem | Primitif publié ; les alias sémantiques et de composants déterminent les rôles réellement appliqués. |
| Radius8 | 1.5rem | Primitif publié ; les alias sémantiques et de composants déterminent les rôles réellement appliqués. |
### Contrat de surcharge de ThemeProvider

| Nom / règle | Valeur / configuration | Utilisation |
| --- | --- | --- |
| theme.semantic | semantic token overrides | Modifiez un rôle tel que BackgroundColorPrimary. |
| theme.component | component token overrides | Limitez une modification à un rôle de composant, par exemple ButtonPrimaryBackgroundColor. |
| value | default/light token value | Fournissez la valeur pour l’apparence claire. |
| extensions.appearance.dark.value | dark appearance value | Indiquez la valeur correspondante pour l’apparence sombre. |
| CSS output | --a2-<kebab-case-name> | ThemeProvider sérialise la clé du jeton dans une variable CSS. |
| Primitive cascade | resolved at build time | La surcharge d’un primitif à l’exécution ne recalcule pas les alias sémantiques. |
## ServiceTitan Anvil — guide complet du système de conception

Un guide pratique pour choisir, appliquer et évaluer ce système dans une interface réelle.

Des conseils complets, un point de départ pour la mise en œuvre et des liens vers les sources.

## ServiceTitan Anvil — architecture de jetons à trois niveaux du système de conception

### Placez les primitifs sous les rôles sémantiques

> Anvil2 distingue les primitifs bruts, les rôles sémantiques et les alias propres aux composants. En règle générale, un produit doit choisir le rôle qui décrit le sens de l’interface plutôt que de reprendre un bleu primitif dans chaque contrôle. Le tableau de référence inclut les valeurs exportées exactes et leurs équivalents pour le thème sombre, afin de permettre l’examen des deux thèmes en parallèle.

### Ne vous attendez pas à un recalcul des alias à l’exécution

Le guide officiel de thématisation précise que les références aux primitives sont résolues lors de la compilation. Modifier une primitive via un fournisseur à l’exécution ne régénère pas automatiquement la couche sémantique. Pour recolorer l’application, remplacez le rôle sémantique concerné ; pour modifier un seul composant, utilisez le niveau du composant. Vous éviterez ainsi un thème partiellement modifié, où des contrôles associés ne seraient plus cohérents.

## Système de design ServiceTitan Anvil : apparences claire et sombre

### Associer les changements de premier plan et d’arrière-plan

L’action principale utilise BackgroundColorPrimary avec ForegroundColorOnPrimary. En apparence claire, le fond est bleu et le texte blanc ; en apparence sombre, le bleu est plus clair et le libellé foncé. Modifier uniquement le fond produirait une combinaison incohérente. L’étude complète HTML utilise light-dark() avec un basculement de color-scheme pour présenter ces valeurs, sans prétendre utiliser ThemeProvider.

### Associer une signification explicite aux couleurs d’état

Anvil2 associe une signification aux familles de couleurs d’état. L’exemple local utilise un rôle de réussite, mais indique précisément ce qui s’est passé. Le technicien indisponible est identifié par un libellé et une option radio désactivée. Aucune teinte verte ne laisse entendre qu’une intervention a été envoyée : l’exemple ne se connecte jamais à un système de gestion des interventions.

## Système de design ServiceTitan Anvil : affectation et état d’une intervention

### L’indisponibilité doit limiter le choix suivant

La sélection d’un technicien remplace les horaires de début proposés par les disponibilités fictives de cette personne. Le technicien indisponible reste visible, mais ne peut pas être sélectionné. Ces deux contrôles sont interdépendants : modifier le premier doit réinitialiser le second à une valeur valide, au lieu de conserver un horaire qui ne correspond pas à la personne choisie.

### Distinguer la planification de l’exécution

La confirmation de l’affectation enregistre un plan local et précise qu’aucune intervention n’a été envoyée. Un processus de production devrait vérifier les autorisations, récupérer les disponibilités à jour et gérer les conflits avant de valider l’affectation. L’exemple rend cette limite explicite tout en proposant une interaction complète, que l’on peut tester et adapter.

## Système de design ServiceTitan Anvil : densité opérationnelle et lisibilité

### Garder le contexte de l’intervention à proximité du choix

Un résumé compact de l’intervention indique la tâche, la durée, le secteur et la priorité. Les techniciens sont présentés dans une liste verticale facile à lire, et l’intervention proposée figure dans un panneau distinct. L’interface utilise des séparateurs neutres et une couleur d’accent discrète pour que le choix et l’action principale restent faciles à repérer. Les barres de planning sont données à titre indicatif ; le sélecteur d’horaire fait foi.

### Préserver l’ordre de lecture sur mobile

Sous 768px, l’intervention proposée apparaît après la tâche et la liste des techniciens. Les contrôles conservent une taille suffisante et les libellés passent naturellement à la ligne. L’aperçu n’utilise aucun panneau à hauteur de fenêtre fixe : il ne crée donc pas de grand espace vide sous un contenu court. La disposition sur une seule colonne suit le même ordre de décision que la mise en page plus large.

## Système de design ServiceTitan Anvil : intégration native et alternative complète HTML

### Utiliser la structure documentée du fournisseur

Le ThemeProvider d’Anvil2 accepte les remplacements de jetons sémantiques et de composants, avec des extensions pour value et l’apparence sombre. Le guide présente ci-dessous un exemple complet de fournisseur, tiré de la documentation publique. Il s’agit d’une référence d’intégration native, distincte de l’exemple HTML autonome exécuté localement.

### Ne pas prétendre qu’un environnement d’exécution du package a été vérifié

Cette page fournit une implémentation HTML fonctionnelle et des données de jetons vérifiées. L’exemple du fournisseur natif est une référence d’intégration distincte ; il ne correspond pas à l’environnement d’exécution affiché dans l’aperçu. Pour utiliser les composants React, installez une version d’Anvil2 compatible, incluez les styles requis et vérifiez les API des composants avant de remplacer les contrôles natifs HTML.

## Système de design ServiceTitan Anvil : accessibilité et vérification des états

### Utiliser la sémantique native des formulaires

Les techniciens sont présentés sous forme de boutons radio natifs avec libellés, l’horaire de début est un sélecteur avec libellé et le contrôle d’apparence expose aria-pressed. Le message de confirmation est une région d’état. Le navigateur gère nativement la sélection des boutons radio au clavier ; le style de focus visible reste indépendant des styles de sélection et de survol.

### Vérifier les changements après confirmation

Le changement de technicien ou d’horaire efface l’ancien message de confirmation. Le changement d’apparence conserve le technicien et l’horaire sélectionnés, car une modification visuelle du thème ne devrait pas effacer un plan. Les tests doivent couvrir ces deux transitions, ainsi que la désactivation d’une option et le débordement sur écran étroit, plutôt que de vérifier uniquement qu’un bouton est cliquable.

## Système de design ServiceTitan Anvil : transfert à l’agent et mise en œuvre

### Commencer par le modèle d’état complet

Enregistrez et exécutez l’exemple HTML complet pour examiner son comportement. Les paires de couleurs claires et sombres dérivées des jetons sont visibles en haut de CSS, et les données de disponibilité définies dans l’exemple figurent dans le script. Une mise en œuvre native de React peut conserver les mêmes transitions d’état tout en utilisant des contrôles Anvil2 vérifiés.

1. Exécutez l’exemple HTML complet.

2. Sélectionnez le deuxième technicien et vérifiez que les horaires de début proposés changent.

3. Confirmez le plan, puis modifiez l’horaire et vérifiez que le message de confirmation est effacé.

4. Changez l’apparence et vérifiez que la sélection actuelle est conservée.

### Préciser les éléments qui nécessitent de vraies données

Demandez à l’agent de conserver la liste d’horaires interdépendante, le membre du personnel désactivé, l’invalidation de la confirmation et le comportement de l’apparence. Ne remplacez les données fictives de l’intervention et du personnel que lorsqu’un contrat de données réel est disponible. Ne transformez pas la confirmation locale en message de réussite en changeant simplement le libellé du bouton.

- Vérifiez les disponibilités avant d’envoyer une intervention.

- Séparez les remplacements sémantiques de ceux des composants.

- Testez les paires de couleurs de premier plan et d’arrière-plan en apparences claire et sombre.

## Système de design ServiceTitan Anvil : exemples de composants

### Exemple complet d’affectation locale

Exemple original HTML/CSS/JavaScript, avec des apparences dérivées des jetons et un état de formulaire interdépendant.

```
<!doctype html>
<html lang="en">
  <meta charset="utf-8" /><meta
    name="viewport"
    content="width=device-width,initial-scale=1"
  /><title>Dispatch assignment study</title
  ><style>
    :root {
      color-scheme: light;
      --surface: light-dark(#fff, #1a1a1a);
      --hover: light-dark(#f9f9f9, #292929);
      --text: light-dark(#040404, #fff);
      --muted: light-dark(#707070, #c4c4c4);
      --line: light-dark(#e6e6e6, #404040);
      --action: light-dark(#0265dc, #70b1ff);
      --on-action: light-dark(#fff, #1a1a1a);
      --faint: light-dark(#fafcff, #000b19);
      --success: light-dark(#077e50, #0bd085);
    }
    * {
      box-sizing: border-box;
    }
    body {
      margin: 0;
      padding: 24px;
      background: var(--surface);
      color: var(--text);
      font:
        16px/1.5 system-ui,
        sans-serif;
    }
    h1 {
      font-size: 28px;
      margin: 8px 0;
    }
    h2 {
      font-size: 20px;
      margin: 0 0 16px;
    }
    p {
      margin: 8px 0 16px;
    }
    .small {
      font-size: 13px;
      color: var(--muted);
    }
    header {
      display: flex;
      justify-content: space-between;
      gap: 24px;
      border-bottom: 1px solid var(--line);
      padding-bottom: 24px;
    }
    button,
    select {
      font: inherit;
      color: inherit;
      background: var(--surface);
      border: 1px solid var(--line);
      border-radius: 4px;
      padding: 10px 16px;
      min-height: 44px;
    }
    button {
      cursor: pointer;
    }
    button:hover {
      background: var(--hover);
    }
    button:focus-visible,
    select:focus-visible,
    input:focus-visible {
      outline: 3px solid var(--action);
      outline-offset: 3px;
    }
    main {
      display: grid;
      grid-template-columns: 1.4fr 1fr;
      gap: 24px;
      margin-top: 24px;
    }
    .board {
      border: 1px solid var(--line);
      border-radius: 8px;
      padding: 24px;
    }
    .job {
      padding: 16px;
      background: var(--faint);
      border-left: 4px solid var(--action);
      margin-bottom: 24px;
    }
    .job strong {
      display: block;
    }
    .people {
      display: grid;
      gap: 12px;
    }
    .person {
      display: grid;
      grid-template-columns: 24px 1fr auto;
      gap: 12px;
      padding: 16px;
      border: 1px solid var(--line);
      border-radius: 4px;
      align-items: center;
    }
    .person:has(:checked) {
      border-color: var(--action);
      background: var(--faint);
    }
    input {
      accent-color: var(--action);
      width: 18px;
      height: 18px;
    }
    .person small {
      display: block;
      color: var(--muted);
    }
    .person:has(:disabled) {
      background: var(--hover);
    }
    .timeline {
      display: flex;
      gap: 4px;
      margin: 20px 0;
    }
    .timeline span {
      height: 40px;
      flex: 1;
      background: var(--line);
      border-radius: 4px;
    }
    .timeline .open {
      background: var(--action);
    }
    .timeline-label {
      display: flex;
      justify-content: space-between;
      font-size: 13px;
      color: var(--muted);
    }
    label[for="time"] {
      display: block;
      margin: 16px 0 8px;
    }
    .primary {
      background: var(--action);
      color: var(--on-action);
      border-color: var(--action);
      margin-top: 24px;
    }
    .primary:hover {
      filter: brightness(0.95);
      background: var(--action);
    }
    #feedback {
      color: var(--success);
      padding-top: 16px;
    }
    footer {
      border-top: 1px solid var(--line);
      padding-top: 16px;
      margin-top: 24px;
      font-size: 13px;
      color: var(--muted);
    }
    @media (max-width: 767px) {
      body {
        padding: 16px;
      }
      main {
        grid-template-columns: 1fr;
      }
      .board {
        padding: 16px;
      }
      header {
        display: block;
      }
      header button {
        margin-top: 8px;
      }
      .person {
        grid-template-columns: 24px 1fr;
      }
      .person > span:last-child {
        grid-column: 2;
      }
    }
  </style>
  <body>
    <header>
      <div>
        <div class="small">SERVICE OPERATIONS / LOCAL PLANNING STUDY</div>
        <h1>Assign a service visit</h1>
        <p>Match a sample job with an available technician.</p>
      </div>
      <div>
        <button id="theme" aria-pressed="false">Dark appearance</button>
      </div>
    </header>
    <main>
      <section class="board">
        <h2>Job and technician</h2>
        <div class="job">
          <strong>Sample job #204 · Heating inspection</strong
          ><span>90 minutes · North district · Routine priority</span>
        </div>
        <div class="people" id="people"></div>
      </section>
      <aside class="board">
        <h2>Proposed visit</h2>
        <p id="technician"></p>
        <label for="time">Available start time</label
        ><select id="time"></select>
        <div class="timeline" aria-hidden="true">
          <span></span><span class="open"></span><span class="open"></span
          ><span></span><span></span><span></span>
        </div>
        <div class="timeline-label">
          <span>08:00</span><span>Illustrative day</span><span>17:00</span>
        </div>
        <p class="small">
          The time list is the source of available choices. The bars are a
          decorative schedule summary.
        </p>
        <button id="assign" class="primary">Review assignment</button>
        <p id="feedback" role="status"></p>
      </aside>
    </main>
    <footer>
      Original HTML using Anvil2 Hammer token 3.5.0 light/dark roles. Fictional
      technicians and schedules; no dispatch service connected.
    </footer>
    <script>
      const staff = [
        {
          id: "alex",
          name: "Alex Morgan",
          skill: "Heating specialist",
          slots: ["09:00", "11:00"],
        },
        {
          id: "sam",
          name: "Sam Rivera",
          skill: "General maintenance",
          slots: ["13:00", "15:00"],
        },
        { id: "lee", name: "Lee Chen", skill: "Unavailable today", slots: [] },
      ];
      let selected = "alex";
      const $ = (id) => document.getElementById(id);
      function update() {
        const p = staff.find((s) => s.id === selected);
        $("technician").textContent = p.name + " · " + p.skill;
        $("time").replaceChildren(...p.slots.map((t) => new Option(t, t)));
        $("feedback").textContent = "";
      }
      $("people").replaceChildren(
        ...staff.map((p) => {
          const l = document.createElement("label");
          l.className = "person";
          const input = document.createElement("input");
          input.type = "radio";
          input.name = "technician";
          input.checked = p.id === selected;
          input.disabled = p.slots.length === 0;
          input.onchange = () => {
            selected = p.id;
            update();
          };
          const text = document.createElement("span");
          text.innerHTML = `<strong>${p.name}</strong><small>${p.skill}</small>`;
          const availability = document.createElement("span");
          availability.textContent = p.slots.length
            ? p.slots.length + " start times"
            : "Unavailable";
          l.append(input, text, availability);
          return l;
        }),
      );
      $("time").onchange = () => {
        $("feedback").textContent = "";
      };
      $("assign").onclick = () => {
        $("feedback").textContent =
          "Local plan: " +
          staff.find((s) => s.id === selected).name +
          " at " +
          $("time").value +
          ". No visit has been dispatched.";
      };
      $("theme").onclick = () => {
        let dark = $("theme").getAttribute("aria-pressed") !== "true";
        $("theme").setAttribute("aria-pressed", dark);
        document.documentElement.style.colorScheme = dark ? "dark" : "light";
      };
      update();
    </script>
  </body>
</html>
```

Référence du code

- Enregistrez sous index.html ; les navigateurs modernes prennent en charge light-dark(). Il ne s’agit pas de l’environnement d’exécution natif d’Anvil2.

### Référence d’intégration de ThemeProvider

Structure documentée des remplacements sémantiques dans un exemple complet de React. À vérifier avec la version d’Anvil2 installée.

```
import React from 'react';
import { createRoot } from 'react-dom/client';
import { ThemeProvider } from '@servicetitan/anvil2';

const theme = {
  semantic: {
    BackgroundColorPrimary: {
      value: '#7c3aed',
      extensions: { appearance: { dark: { value: '#a78bfa' } } },
    },
  },
};

function App() {
  return (
    <ThemeProvider theme={theme}>
      <main>
        <h1>Theme integration check</h1>
        <div style={{ background: 'var(--a2-background-color-primary)', padding: 24 }}>
          <span style={{ color: 'var(--a2-foreground-color-on-primary)' }}>
            Primary role with the provider’s matching foreground role
          </span>
        </div>
      </main>
    </ThemeProvider>
  );
}
createRoot(document.getElementById('root')).render(<App />);
```

Référence du code

- Cette référence au fournisseur a été vérifiée à partir de la documentation officielle ; l’exemple en cours d’exécution ci-dessus est indépendant HTML.

## Configuration de l’agent IA système de design ServiceTitan Anvil

### Point de départ de l’implémentation

Aucun package Anvil2 n’est nécessaire pour l’exemple exécuté. L’exemple distinct avec ThemeProvider nécessite une installation native compatible.

```
# Save the complete HTML example as index.html
python3 -m http.server 8080
# Open http://localhost:8080
```

### Un prompt pour votre projet

```
Créez une interface d’affectation des interventions à l’aide des rôles sémantiques Anvil2. Reprenez l’exemple complet : paires de couleurs de premier plan pour les thèmes clair et sombre, horaires proposés selon le technicien, personnel indisponible désactivé et validation réinitialisée. Choisissez explicitement entre l’installation originale HTML et une installation Anvil2 vérifiée React. Ne prétendez jamais qu’une intervention a été affectée sans réponse du backend.
```

## Sources et périmètre du système de design ServiceTitan Anvil

Guide OpenDesign consacré à ServiceTitan Anvil2. Les valeurs des jetons sont extraites de Hammer, le package public de jetons, version 3.5.0. L’exemple en cours d’exécution est original HTML, et non un environnement d’exécution Anvil2 React ; les techniciens et leurs disponibilités sont fictifs.

- Thématisation Niveaux de jetons, substitutions et valeurs d’apparence.

- Couleurs Rôles d’état et conseils sur les couleurs des interactions.

- Espacement Échelle d’espacement et intégration des utilitaires.

- Dimensions Fondamentaux des dimensions.

- Architecture de la bibliothèque Limites du package et de la bibliothèque Anvil2.

## Ressources connexes au système de design ServiceTitan Anvil

### Siemens iX

Examinez des événements industriels à l’aide d’un vocabulaire explicite de niveaux de gravité, de rôles de couleur adaptés au thème et de la liste d’événements iX API. Ce guide crée un carnet d’équipement local sans confondre un événement révisé avec une alarme machine résolue.

### Sprout Social Seeds

Examinez les véritables tokens publics de Seeds pour les couleurs, l’espacement, la typographie des produits et les bordures dans une file complète de révision éditoriale, avec sélection mixte, états vides après filtrage et annulation.

### SumUp Circuit

Un guide Circuit avec des tokens sémantiques exacts, des propriétés natives de validation, un éditeur complet de fiches marchand et un aperçu illustré côté client.


- [Référence de mise en œuvre](https://anvil.servicetitan.com/docs/web/foundations/theming)
- [Source](https://anvil.servicetitan.com/docs/web/foundations/theming)
- [Source](https://anvil.servicetitan.com/docs/web/foundations/sizing)
- [Référence du code](https://anvil.servicetitan.com/docs/web/foundations/theming)
- [Thématisation Niveaux de jetons, substitutions et valeurs d’apparence.](https://anvil.servicetitan.com/docs/web/foundations/theming)
- [Couleurs Rôles d’état et conseils sur les couleurs des interactions.](https://anvil.servicetitan.com/docs/web/foundations/colors)
- [Espacement Échelle d’espacement et intégration des utilitaires.](https://anvil.servicetitan.com/docs/web/foundations/spacing)
- [Dimensions Fondamentaux des dimensions.](https://anvil.servicetitan.com/docs/web/foundations/sizing)
- [Architecture de la bibliothèque Limites du package et de la bibliothèque Anvil2.](https://anvil.servicetitan.com/docs/library-architecture)
