Gestion des services · Nouvelle ressource

ServiceTitan Anvil — système de conception.

Concrétisez Anvil2 avec son modèle de tokens à trois niveaux, ses valeurs de rôles vérifiées en mode clair et sombre, et une interaction complète d’affectation de service avec des états de technicien, d’horaire et de révision synchronisés.

Guide OpenDesign7 chapitres38 entrées de référenceGuide de l’application d’origine
01

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

Associer un technicien à une visite de serviceOuvrir l’exemple complet
HTML d’origine, avec les valeurs de rôle des jetons Hammer 3.5.0. · Référence de mise en œuvre
02

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

Source
Nom / règleValeur / configurationUtilisation
BackgroundColorDefaultlight #ffffff / dark #1a1a1aSurface principale de l’application.
BackgroundColorDefaultHoverlight #f9f9f9 / dark #292929Surface au survol.
BackgroundColorPrimarylight #0265dc / dark #70b1ffFond de l’action principale.
BackgroundColorPrimaryHoverlight #1d4ca3 / dark #9ecaffRôle natif pour l’état de survol principal.
BackgroundColorPrimaryFaintlight #fafcff / dark #000b19Surface de sélection discrète.
ForegroundColorSubduedlight #707070 / dark #c4c4c4Texte d’accompagnement.
ForegroundColorOnPrimarylight #ffffff / dark #1a1a1aTexte associé au remplissage principal.
ForegroundColorPrimarylight #0265dc / dark #70b1ffPremier plan interactif.
BorderColorSubduedlight #e6e6e6 / dark #404040Bordures peu mises en avant.
BorderColorPrimarylight #0265dc / dark #9ecaffBordure interactive.
StatusColorInfolight #0265dc / dark #70b1ffÉtat informatif.
StatusColorDangerlight #d62100 / dark #ff5b3dAttention immédiate.
StatusColorSuccesslight #077e50 / dark #0bd085Vérification locale terminée.
StatusColorWarninglight #ffbe00 / dark #ffe278Mise en garde sans urgence.

Géométrie primitive et valeurs adaptatives

Source
Nom / règleValeur / configurationUtilisation
BreakpointSm640pxPrimitif publié ; les alias sémantiques et de composants déterminent les rôles réellement appliqués.
BreakpointMd768pxPrimitif publié ; les alias sémantiques et de composants déterminent les rôles réellement appliqués.
BreakpointLg1024pxPrimitif publié ; les alias sémantiques et de composants déterminent les rôles réellement appliqués.
BreakpointXl1280pxPrimitif publié ; les alias sémantiques et de composants déterminent les rôles réellement appliqués.
BreakpointXxl1536pxPrimitif publié ; les alias sémantiques et de composants déterminent les rôles réellement appliqués.
DurationDefault200msPrimitif publié ; les alias sémantiques et de composants déterminent les rôles réellement appliqués.
DurationInstant0msPrimitif publié ; les alias sémantiques et de composants déterminent les rôles réellement appliqués.
DurationFast100msPrimitif publié ; les alias sémantiques et de composants déterminent les rôles réellement appliqués.
DurationSlow300msPrimitif publié ; les alias sémantiques et de composants déterminent les rôles réellement appliqués.
Radius00remPrimitif publié ; les alias sémantiques et de composants déterminent les rôles réellement appliqués.
Radius10.1875remPrimitif publié ; les alias sémantiques et de composants déterminent les rôles réellement appliqués.
Radius20.375remPrimitif publié ; les alias sémantiques et de composants déterminent les rôles réellement appliqués.
Radius30.5625remPrimitif publié ; les alias sémantiques et de composants déterminent les rôles réellement appliqués.
Radius40.75remPrimitif publié ; les alias sémantiques et de composants déterminent les rôles réellement appliqués.
Radius50.9375remPrimitif publié ; les alias sémantiques et de composants déterminent les rôles réellement appliqués.
Radius61.125remPrimitif publié ; les alias sémantiques et de composants déterminent les rôles réellement appliqués.
Radius71.3125remPrimitif publié ; les alias sémantiques et de composants déterminent les rôles réellement appliqués.
Radius81.5remPrimitif publié ; les alias sémantiques et de composants déterminent les rôles réellement appliqués.

Contrat de surcharge de ThemeProvider

Source
Nom / règleValeur / configurationUtilisation
theme.semanticsemantic token overridesModifiez un rôle tel que BackgroundColorPrimary.
theme.componentcomponent token overridesLimitez une modification à un rôle de composant, par exemple ButtonPrimaryBackgroundColor.
valuedefault/light token valueFournissez la valeur pour l’apparence claire.
extensions.appearance.dark.valuedark appearance valueIndiquez 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 cascaderesolved at build timeLa surcharge d’un primitif à l’exécution ne recalcule pas les alias sémantiques.
03

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.

DESIGN-servicetitan-anvil.md

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

Télécharger le guide

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.

Extrait de ce guide OpenDesign

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

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.

html
<!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.

jsx
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.
04

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.

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

Un prompt pour votre projet

Prompt pour l’agent IA
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.
05

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.